mbed based IoT Gateway More details http://blog.thiseldo.co.uk/wp-filez/IoTGateway.pdf
Dependencies: NetServices FatFileSystem csv_parser mbed MQTTClient RF12B DNSResolver SDFileSystem
Diff: main.cpp
- Revision:
- 0:a29a0225f203
- Child:
- 2:27714c8c9c0a
diff -r 000000000000 -r a29a0225f203 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Apr 02 22:05:20 2012 +0000 @@ -0,0 +1,401 @@ +/** IoT Gateway main code + * + * @author Andrew Lindsay + * + * @section LICENSE + * + * Copyright (c) 2012 Andrew Lindsay (andrew [at] thiseldo [dot] co [dot] uk) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @section DESCRIPTION + * + * Code is to be classed as beta. There is a lot of debug code still includes + * to dump heap sizes and other values. It is still a work in progress and + * should be treated as such. + * + * Sample configuration file IOTSETUP.TXT + * + * @code + * pachube.key=***your pachube api key*** + * ip.mode=fixed + * ip.address=192.168.1.99 + * ip.netmask=255.255.255.0 + * ip.gateway=192.168.1.1 + * ip.dns=192.168.1.1 + * rfm12b.band=868 + * rfm12b.id=30 + * rfm12b.group=212 + * time.timezone=GMT + * time.dst=yes + * mqtt.host= + * mqtt.port= + * @endcode + * + * Some values are not yet used + * + */ + + +#include "mbed.h" +#include "SDFileSystem.h" +#include "EthernetNetIf.h" +#include "NTPClient.h" +#include "RF12B.h" +#include "IoTRouting.h" + + +using std::string; + +#define VERSION_INFO "IoT Gateway Basic - Version 0.4-BETA " + +DigitalOut heartbeatLED(LED1, "heartbeatLED"); +DigitalOut led2(LED2, "led2"); +DigitalOut led3(LED3, "led3"); +//DigitalOut led4(LED4, "led4"); +DigitalOut linkLED(p30, "linkLED"); +DigitalOut statusLED(p25, "statusLED"); + +// Setup which filesystem to use, Local or uSD card +__attribute((section("AHBSRAM0"))) LocalFileSystem fs("iotfs"); +//__attribute((section("AHBSRAM0"))) SDFileSystem sd(p5, p6, p7, p8, "iotfs"); + +__attribute((section("AHBSRAM0"))) EthernetNetIf *eth; +__attribute((section("AHBSRAM0"))) NTPClient ntp; +__attribute((section("AHBSRAM0"))) Ethernet ethernet; + +// Configuration values loaded from file iotsetup.dat +__attribute((section("AHBSRAM0"))) IpAddr ipAddress(0, 0, 0, 0); +__attribute((section("AHBSRAM0"))) IpAddr netMask(255, 255, 255, 0); +__attribute((section("AHBSRAM0"))) IpAddr gwAddress(0, 0, 0, 0); +__attribute((section("AHBSRAM0"))) IpAddr dnsAddress(0, 0, 0, 0); +__attribute((section("AHBSRAM0"))) bool useDHCP = false; + +// Static buffers +#define MAX_LINE_LENGTH 128 +__attribute__((section("AHBSRAM0"))) static char lineBuf[MAX_LINE_LENGTH]; +__attribute__((section("AHBSRAM0"))) static OutputPachube outPachube; +__attribute__((section("AHBSRAM0"))) static IoTRouting rtr; + +// Pachube config +__attribute__((section("AHBSRAM0"))) static char pachubeApiKey[65]; + +// MQTT config +__attribute((section("AHBSRAM0"))) IpAddr mqttHostAddress( 0, 0, 0, 0); +//char mqttHostName[65]; + + +// RFM12B config +__attribute((section("AHBSRAM0"))) static string rfm12bBands[4] = {"xxx", "433", "868", "915" }; +__attribute((section("AHBSRAM0"))) uint8_t rfm12bId; +__attribute((section("AHBSRAM0"))) uint8_t rfm12bBand; +__attribute((section("AHBSRAM0"))) uint8_t rfm12bGroup; + +#define iotConfigFile "/iotfs/IOTSETUP.TXT" +#define startUrlFile "/iotfs/START.URL" + +__attribute((section("AHBSRAM0"))) RF12B rfm12b(p11, p12, p13, p14, p18); + +__attribute((section("AHBSRAM0"))) Serial pc(USBTX, USBRX); // tx, rx + +// Utility functions + + +/** convert string to IP address + * + * @param ipAddrInt IP address as single 32bit integer + * @param value The IP address as a string + */ +void setIpAddress( int *ipAddrInt, char *value ) { + sscanf(value, "%d.%d.%d.%d", &ipAddrInt[0],&ipAddrInt[1],&ipAddrInt[2],&ipAddrInt[3]); +} + +/** Get the value from a name=value pair + * + * @param buf The name/value pair character buffer + * @returns pointer to value + */ +char *getConfigValue( char *buf ) { + char *ptr = strchr(buf, '='); + if ( ptr == NULL ) + return NULL; + ptr++; + return ptr; +} + +/** Copy zero or CR/LF terminated string to new zero terminated string + * Used when reading config values from files to remove CR/LF endings + * + * @param to Char pointer to destination + * @param from Char pointer to source + */ +void strcpynull(char *to, char *from ) { + while ( *from != '\n' && *from != '\r' && *from != '\0' ) { + *to++ = *from++; + } + *to = '\0'; +} + + +/** Read IoT gateway configuration file + * File is of format name=value + * + */ +bool readConfig() { + + FILE *fp = fopen(iotConfigFile, "r"); + if (fp == NULL) { + printf("Could not open file %s for read\n", iotConfigFile); + return false; + } + // read file + while (!feof( fp )) { + fgets(lineBuf,MAX_LINE_LENGTH,fp); + //printf("%s",lineBuf); + // Need to read each entry and update config + char *nameStr; + char *valueStr; + nameStr = lineBuf; + valueStr = strchr( lineBuf, '=' ); + if ( valueStr != NULL ) { + *valueStr++ = '\0'; + int tmpAddress[4] = {0,0,0,0}; + if ( strcmp( nameStr, "ip.address" ) == 0 ) { + setIpAddress( &tmpAddress[0], valueStr ); + ipAddress = IpAddr( tmpAddress[0],tmpAddress[1],tmpAddress[2],tmpAddress[3]); + } else if ( strcmp( nameStr, "ip.netmask" ) == 0 ) { + setIpAddress( &tmpAddress[0], valueStr ); + netMask = IpAddr( tmpAddress[0],tmpAddress[1],tmpAddress[2],tmpAddress[3]); + } else if ( strcmp( nameStr, "ip.gateway" ) == 0 ) { + setIpAddress( &tmpAddress[0], valueStr ); + gwAddress = IpAddr( tmpAddress[0],tmpAddress[1],tmpAddress[2],tmpAddress[3]); + } else if ( strcmp( nameStr, "ip.dns" ) == 0 ) { + setIpAddress( &tmpAddress[0], valueStr ); + dnsAddress = IpAddr( tmpAddress[0],tmpAddress[1],tmpAddress[2],tmpAddress[3]); + } else if ( strcmp( nameStr, "pachube.key" ) == 0 ) { + strcpynull(pachubeApiKey, valueStr ); + } else if ( strcmp( nameStr, "ip.mode" ) == 0 ) { + useDHCP = (strncmp( valueStr, "dhcp", 4) == 0 ? true : false); + } else if ( strcmp( nameStr, "rfm12b.band" ) == 0 ) { + if ( strncmp( valueStr, "433", 3 ) == 0 ) + rfm12bBand = RF12_433MHZ; + else if ( strncmp( valueStr, "868", 3 ) == 0 ) + rfm12bBand = RF12_868MHZ; + else if ( strncmp( valueStr, "915", 3 ) == 0 ) + rfm12bBand = RF12_915MHZ; + } else if ( strcmp( nameStr, "rfm12b.id" ) == 0 ) { + rfm12bId = atoi( valueStr ); + } else if ( strcmp( nameStr, "rfm12b.group" ) == 0 ) { + rfm12bGroup = atoi( valueStr ); + } + } + } + fclose(fp); + + return true; +} + +/** Write the current config to file + */ +bool writeConfig() { + +// Doesnt work? + // Rename original file +// if( rename ( iotConfigFile.c_str(), iotConfigFileBak.c_str() ) != 0 ) { +// printf("Could not rename file %s\n", iotConfigFile); +// return false; +// } + + FILE *fp = fopen(iotConfigFile, "w"); + if (fp == NULL) { + printf("Could not open file %s for write\n", iotConfigFile); + return false; + } + + time_t ctTime; + ctTime = time(NULL); + + fprintf(fp, "# iotsetup created (UTC) %s\n", ctime(&ctTime)); + fprintf(fp, "pachube.key=%s\n", pachubeApiKey ); + fprintf(fp, "ip.mode=%s\n", (useDHCP ? "dhcp" : "fixed") ); + // Add msg to say net config being ignored + if ( useDHCP ) + fprintf(fp, "# Following ip.* parameters not used in DHCP mode\n"); + + fprintf(fp, "ip.address=%hhu.%hhu.%hhu.%hhu\n", ipAddress[0], ipAddress[1],ipAddress[2],ipAddress[3]); + fprintf(fp, "ip.netmask=%hhu.%hhu.%hhu.%hhu\n",netMask[0], netMask[1], netMask[2], netMask[3]); + fprintf(fp, "ip.gateway=%hhu.%hhu.%hhu.%hhu\n",gwAddress[0],gwAddress[1],gwAddress[2],gwAddress[3]); + fprintf(fp, "ip.dns=%hhu.%hhu.%hhu.%hhu\n",dnsAddress[0],dnsAddress[1],dnsAddress[2],dnsAddress[3]); + + fprintf(fp, "rfm12b.band=%s\n",rfm12bBands[rfm12bBand].c_str()); + fprintf(fp, "rfm12b.id=%d\n",rfm12bId); + fprintf(fp, "rfm12b.group=%d\n",rfm12bGroup); + + fprintf(fp, "time.timezone=GMT\n"); + fprintf(fp, "time.dst=yes\n"); + fclose(fp); + + return true; +} + + +// These external symbols are maintained by the linker to indicate the +// location of various regions in the device's memory. They will be used by +// DisplayRAMBanks() to dump the size of each RAM bank to stdout. +extern unsigned int Image$$RW_IRAM1$$Base; +extern unsigned int Image$$RW_IRAM1$$ZI$$Limit; +extern unsigned int Image$$RW_IRAM2$$Base; +extern unsigned int Image$$RW_IRAM2$$ZI$$Limit; +extern unsigned int Image$$RW_IRAM3$$Base; +extern unsigned int Image$$RW_IRAM3$$ZI$$Limit; + +// Displays the size of static allocations for each RAM bank as indicated by +// ARM linker to stdout. +static void DisplayRAMBanks(void) { + printf("Static RAM bank allocations\r\n"); + printf(" Main RAM = %u\r\n", (unsigned int)&Image$$RW_IRAM1$$ZI$$Limit - + (unsigned int)&Image$$RW_IRAM1$$Base); + printf(" RAM0 = %u\r\n", (unsigned int)&Image$$RW_IRAM2$$ZI$$Limit - + (unsigned int)&Image$$RW_IRAM2$$Base); + printf(" RAM1 = %u\r\n", (unsigned int)&Image$$RW_IRAM3$$ZI$$Limit - + (unsigned int)&Image$$RW_IRAM3$$Base); +} + +/** Main function, where all the magic starts + */ +int main() { + linkLED = 1; + statusLED = 0; + pc.baud(115200); + + printf(VERSION_INFO); + printf("\n"); + + DisplayRAMBanks(); + + printf("Setting up...\n"); + printf("\nHEAP STATS\n"); + __heapstats((__heapprt)fprintf,stderr); + + if ( !readConfig() ) { + error("Setup failed"); + } + + if (useDHCP) { + printf("Using DHCP\n"); + eth = new EthernetNetIf( "IoTGateway" ); + } else { +// printf("Using Fixed addressing\n"); + eth = new EthernetNetIf( ipAddress, netMask, gwAddress, dnsAddress ); + } + EthernetErr ethErr = eth->setup(); + if (ethErr) { + printf("Error %d in setup.\n", ethErr); + return -1; + } + + linkLED = !ethernet.link(); + + if (useDHCP) { + // We are using dhcp so get IP Address + ipAddress = eth->getIp(); + } + + + // Get Current time + Host server(IpAddr(), 123, "0.uk.pool.ntp.org"); + ntp.setTime(server); + + time_t ctTime = time(NULL); + printf("\nTime is now (UTC): %s\n", ctime(&ctTime)); + + outPachube = OutputPachube(); + outPachube.setApiKey( pachubeApiKey ); +// IpAddr serverIpAddr(192,168,1,77); +// OutputMqtt outMqtt = OutputMqtt(); +// outMqtt.setHost( &serverIpAddr ); + +// printf("MAIN: outPachube = %ld\n",(int)&outPachube); + rtr.addOutput( (OutputDef*)&outPachube , 1); +// rtr.addOutput( (OutputDef*)&outMqtt , 2); + + rtr.initRouting(); + + printf("Setup OK\n"); + + printf( "Setting RFM12B ID %d, Band %d Group %d\n",rfm12bId, rfm12bBand, rfm12bGroup); + rfm12b.init(rfm12bId, rfm12bBand, rfm12bGroup ); //id = 2, band 866, group 5 + + printf("Listening...\n"); + + Timer tm; + tm.start(); + + printf("\nHEAP STATS\n"); + __heapstats((__heapprt)fprintf,stderr); + + short dataLen = 0; + uint8_t *rf12Buf; + + // Start receiving data + rfm12b.rf12_recvStart(); + while (true) { + Net::poll(); + + // This is RFM12B specific + if ( rfm12b.available() > 0 ) { + statusLED = 1; + rf12Buf = rfm12b.get_data(); + dataLen = rfm12b.length(); + + if ( rtr.routePayload( rf12Buf, dataLen + 3 ) ) { + // Successfully dealt with a packet + + // Do something different if routed + + } + + // For now, acknowledge everything + + // Send an ack if required + if ((rf12Buf[1] & ~RF12_HDR_MASK) == RF12_HDR_ACK // && + //(config.nodeId & 0x20) == 0 + ) { + + printf("RFM12B -> ack\n"); + + byte addr = rf12Buf[1] & RF12_HDR_MASK; + rfm12b.rf12_sendStart(RF12_HDR_CTL | RF12_HDR_DST | addr, 0, 1); + } + + statusLED = 0; + } + + if (tm.read()>.5) { + heartbeatLED=!heartbeatLED; //Show that we are alive + tm.start(); + } + + linkLED = !ethernet.link(); + + } + +} + +// The End! \ No newline at end of file