Wednesday 25 April 2012

mBed - Google API source code

A few people have asked me to post the source code. Here it is. Even though I have been programming for years in various database and IBM mainframe languages, I've never done much C, so feel free to send me a few tips. I'm not sure why, for example, it works only when ctTime and buffer[21] are declared outside of the subroutine they're used in??


All the libraries used can all be found on the mbed website.


#include "mbed.h"
#include "DS18B20.h"
#include "DS18S20.h"
#include "OneWireDefs.h"
#include "EthernetNetIf.h"
#include "HTTPClient.h"
#include "NTPClient.h"

#define THERMOMETER DS18S20
#define HOSTNAME "mbed"
string calendar = "<<< YOUR CALENDAR PRIVATE ADDRESS >>>";

EthernetNetIf eth(HOSTNAME);
HTTPClient http;
NTPClient ntp;
THERMOMETER device(true, false, false, p21);

DigitalOut led1(LED1, "led1");
DigitalOut led2(LED2, "led2");
DigitalOut led3(LED3, "led3");
DigitalOut led4(LED4, "led4");

DigitalOut heater(p10);
time_t ctTime;
char buffer[21];
int targetTemp = 0;

string getURL() {
    char url[256];
    char endTime[21];
    char startTime[21];
    ctTime = time(NULL);
    strftime(buffer, 20, "%Y-%m-%dT%H:%M:00Z", localtime(&ctTime));
    strcpy(startTime,buffer);
    ctTime = time(NULL)+60;
    strftime(buffer, 20, "%Y-%m-%dT%H:%M:00Z", localtime(&ctTime));
    strcpy(endTime,buffer);      
    //printf("start %s\n",startTime);
    //printf("end   %s\n",endTime);
    sprintf(url, "%s?start-min=%s&start-max=%s", calendar, startTime, endTime);
    //printf("url   %s\n",url);
    return url;
}

string getPage(char url[256]) {
    printf("\nQuery Google API... \n");
    HTTPText txt("text/html",5000);
    HTTPResult r = http.get(url, &txt);
  
    if (r==HTTP_OK) {
       printf("Result Ok\n");
       led2 = 0;
       return txt.gets();
    } else {
       printf("Error %d\n",r);
       led2 = 1;
       return "FAIL";
    }
}

void getAPI() {
    string testres;
    string results;
    char to[3];
    char url[256];
    strcpy(url,(char*)getURL().c_str());
    results = getPage(url);
    //printf("results API %s\n",results);
   
    if (results == "FAIL") {
       printf("%s\n","Bad Result");
       // Make no changes to the target temperature
    } else {
       testres = strstr(results.c_str(),"<title type='html'>");
       //printf("\n\n\n%s\n",testres);
       if (testres == "") {
          // no temperature set for this time
          printf("%s\n","No calendar entry for this time");
          targetTemp = 0;
          led3 = 0;
       } else {
          // grab the temperature (this assumes I've entered a 2 digit int in the event title)
          strncpy(to, testres.c_str()+19, 2);
          printf("result: %s\n", to);
          targetTemp = atoi(to);
          led3 = 1;
       }
    }
}

void getNTP() {
    ctTime = time(NULL);
    printf("Current time is (UTC): %d %s\n", ctTime, ctime(&ctTime));
    printf("NTP setTime...\n");
    Host server(IpAddr(), 123, "pool.ntp.org");
    printf("Result : %d\n", ntp.setTime(server));
    ctTime = time(NULL);
    printf("Time is now (UTC): %d %s\n", ctTime, ctime(&ctTime));
}

void checkTemperature() {
    float temp = device.readTemperature();
    if (temp > (targetTemp+0.5)) {
       heater = 0;
       led4 = 0;
    }
    if (temp < targetTemp) {
       heater = 1;
       led4 = 1;
    }
}

int main() {
    EthernetErr ethErr;
    int count = 0;
    do {
        printf("Setting up %d...\n", ++count);
        ethErr = eth.setup();
        if (ethErr) printf("Timeout\n", ethErr);
    } while (ethErr != ETH_OK);

    printf("Connected OK\n");
    const char* hwAddr = eth.getHwAddr();
    printf("HW address : %02x:%02x:%02x:%02x:%02x:%02x\n",
           hwAddr[0], hwAddr[1], hwAddr[2],
           hwAddr[3], hwAddr[4], hwAddr[5]);

    IpAddr ethIp = eth.getIp();
    printf("IP address : %d.%d.%d.%d\n", ethIp[0], ethIp[1], ethIp[2], ethIp[3]);
    printf("Check router DHCP table for name : %s\n", eth.getHostname());

    getNTP();
    getAPI();
   
    Timer pollTemp;
    pollTemp.start();
    Timer pollAPI;
    pollAPI.start();
    Timer pollNTP;
    pollNTP.start();
    Timer lookAlive;
    lookAlive.start();
   
    while (true) {
        if (lookAlive.read() > 1) {
           led1 = !led1;
           lookAlive.start();
        }
        if (pollTemp.read() > 10) {
            checkTemperature();
            pollTemp.start();
        }
        if (pollAPI.read() > 30) {
            getAPI();
            printf("Target Temp: %i\n",targetTemp);
            pollAPI.start();
        }
        if (pollNTP.read() > 21000) {
            pollNTP.start();
            getNTP();
        }
    }
}

12 comments:

  1. Does mbed has built in ethernet support??

    ReplyDelete
    Replies
    1. It has an ethernet chip, but no magnetics hence the magjack connector. Lots of info available at mbed.org

      Delete
  2. Most HVAC systems employ a deadband of about 2-4 degrees to lengthen the life of your equipment. The stops and starts really kill the hardware.

    ReplyDelete
  3. Thanks, thedoormouse, As you can see I've given myself half a degree, but I'll extend this a bit more. On my beer fridge controller I've got a wider range but 4 degrees on a heater may be a bit much for me to bare! :)

    ReplyDelete
  4. > ctTime and buffer[21] are declared outside of the subroutine

    That is because those variables will be initialized EACH time you call the routine if they are declared inside the function, if you declare them OUTSIDE the routine (in GLOBAL space that is) the variables will be initialized only ONCE (on application startup).

    But nice idea and work!

    ReplyDelete
  5. Great idea, great job! Thanks for sharing!

    ReplyDelete
  6. mbed + coolComponents company low cost mainboard which adds RJ45, USB, etc.

    ReplyDelete
  7. This is a really nice project, thanks for posting the code.
    Could you not post it to the mbed cookbook ?
    I tried compiling it to have a play with your marvelous code , problem is I don't know what version of EtherNetIF.h your using so imported the newest i could find ,, which spat up errors.

    If you post it to mbed to import then the right libs we be in the project.

    Thanks T

    ReplyDelete
  8. Hey,


    Great project, very interesting.

    I am actually trying to do something similar with Arduino.

    What I didn't understand is where do you get the private calendar address?

    Also do you need SSL (https in the address) since I am not sure if I have support for that in Arduino?


    Thanks,
    D.

    ReplyDelete
  9. Just checked you don't need to use HTTPS ,, HTTP:// works fine.
    private address can be found in calendar settings for that calendar (towards bottom of screen)
    I haven't got this code to work yet (library problems and compiler errors), but I am working on a better less memory intense version with ideas from the code of this version.

    ReplyDelete
  10. Got it, thanks.

    I will get to work on the code for Arduino with a WiFly shield.

    I assume it is the XML feed private address, correct?



    Thanks,
    D.

    ReplyDelete
  11. Yeah it's XML version private addy
    but to be honest the API isn't working as expected

    ?start-min=2012-05-09T18:46:00Z&start-max=2012-05-09T18:47:00Z

    still shows entries earlier than ?start-min=2012-05-09TXX:XX

    Though I have reworked the code to parse a page of any length with a 256 byte buffer

    HTTPText txt("text/html",5000);

    5000 bytes original is an awful lot of mem, unfortunately my C++ skills are poor so i'm kinda plodding through it.

    I need to look at the API link

    ReplyDelete