pce.c
author viric <viriketo@gmail.com>
Wed, 21 Dec 2011 22:26:20 +0100
changeset 18 f85465640dd4
parent 17 021968512187
child 19 3ed36b0e41bd
permissions -rw-r--r--
Making pce print timestamps in the download.

/*
    PCE-HT71 programmer and data downloader for libusb
    Copyright (C) 2010-2011  LluĂ­s Batlle i Rossell

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */
#include <usb.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>

const int vendor=0x10c4;
const int product=0x0003;

usb_dev_handle *handle;
struct usb_device *device;

int ep_in = 1;
int ep_out = 2;


/* Downloading, the base timestamp */
time_t timestamp;
int time_per_sample;

enum
{
    HELP,
    SET,
    DOWNLOAD
} action;

struct
{
    int nsamples;
    int time_per_sample;
    int immediate;
    char *download_file;
} config;

FILE *outfile;

int connect()
{
    struct usb_bus *bus;
    bus = usb_get_busses();

    while (bus != 0)
    {
        struct usb_device *dev;
        dev = bus->devices;
        while(dev != 0)
        {
            if (dev->descriptor.idVendor == vendor
                && dev->descriptor.idProduct == product)
            {
                handle = usb_open(dev);
                device = dev;
                return 0;
            }
            dev = dev->next;
        }
        bus = bus->next;
    }

    handle = 0;
    return -1;
}

int send_program()
{
    int res;
    res = usb_bulk_write(handle, ep_out, "\x01\x40\x00", 3, 1000);
    assert(res == 3);
}

int send_config()
{
    int res;
    char buf[64];
    struct tm *t;
    time_t tmp_time;
    tmp_time = time(NULL);
    t = localtime(&tmp_time);

    buf[0] = '\xce';
    buf[1] = 0;
    buf[2] = 0;
    buf[3] = 0;

    // Samples
    *( (int *) &buf[4]) = config.nsamples;
    // Samples got
    *( (int *) &buf[8]) = 0;
    // Sample rate
    *( (int *) &buf[12]) = config.time_per_sample;
    // Year
    *( (int *) &buf[16]) = t->tm_year + 1900;
    // Mintemp
    buf[20] = 0;
    buf[21] = 0;
    *( (short int *) &buf[22]) = 0x4120;
    // Maxtemp
    buf[24] = 0;
    buf[25] = 0;
    *( (short int *) &buf[26]) = 0x41a0;

    // Month
    buf[28] = t->tm_mon;
    // Day
    buf[29] = t->tm_mday;
    // hour
    buf[30] = t->tm_hour;
    // Minute
    buf[31] = t->tm_min;
    // second
    buf[32] = t->tm_sec;
    // Celsius (1 F)
    buf[33] = 0;
    // Pause between leds (300s)
    buf[34] = 0x1e;
    // Name
    buf[35] = 'A';
    buf[36] = '0';

    // Immediate
    buf[51] = config.immediate ? 2 : 1;
    // Minhum
    buf[52] = 0;
    buf[53] = 0;
    *( (short int *) &buf[54]) = 0x410f;
    // Maxhum
    buf[56] = 0;
    buf[57] = 0;
    *( (short int *) &buf[58]) = 0x4296;
    // final
    buf[60] = 0xce;
    buf[61] = 0x0;
    buf[62] = 0x0;
    buf[63] = 0x0;

    res = usb_bulk_write(handle, ep_out, buf, 64, 1000);
    assert(res == 64);

    res = usb_bulk_read(handle, ep_in, buf, 1, 1000);
    assert(res == 1);
}

void parse_data(char *data)
{
    time_t now;
    struct tm *tm_now;
    struct tm tm;

    now = time(0);

    tm_now = localtime(&now);
    memset(&tm, 0, sizeof(tm));

    tm.tm_isdst = tm_now->tm_isdst;
    tm.tm_year = *((short int *) (data + 16)) - 1900;
    tm.tm_mon = *((unsigned char *) (data + 28));
    tm.tm_mday = *((unsigned char *) (data + 29));
    tm.tm_hour = *((unsigned char *) (data + 30));
    tm.tm_min = *((unsigned char *) (data + 31));
    tm.tm_sec = *((unsigned char *) (data + 32));

    timestamp = mktime(&tm);

    fprintf(outfile, "# Start: %i-%i-%i %i:%i:%i @%llu\n",
            tm.tm_year + 1900,
            tm.tm_mon+1,
            tm.tm_mday,
            tm.tm_hour,
            tm.tm_min,
            tm.tm_sec,
            (unsigned long long) timestamp);

    fprintf(outfile, "# Num Samples: %hi\n",
            *((short int *) (data + 8))
            );

    time_per_sample = *((short int *) (data + 12));

    fprintf(outfile, "# Time between samples: %i s\n",
            time_per_sample);
}

int
start_download()
{
    int res;

    res = usb_bulk_write(handle, ep_out, "\x00\x10\x01", 3, 1000);
    assert(res == 3);

    char data[100];
    res = usb_bulk_read(handle, ep_in, data, 3, 1000);
    assert(res == 3);
    fprintf(stderr, "Debug: %02hhx%02hhx%02hhx\n", data[0], data[1], data[2]);
    assert(data[0] == 2);

    int num = *((short int*) &data[1]);
    fprintf(stderr, "Found %i bytes = %i samples\n", num, num/4);

    if (num > 0)
    {
        int num_frames = (num + 63) / 64;
        fprintf(stderr, "%i frames\n", num_frames);

        /*
        res = usb_bulk_write(handle, ep_out, "\x00\x00\x01", 3, 1000);
        assert(res == 3);
        */

        res = usb_bulk_read(handle, ep_in, data, 64, 1000);
        assert(res == 64);

        parse_data(data);

        int sample = 0;
        int block = 0;
        do
        {
            char frame[3];
            int step_frames = num_frames >= 0x40 ? 0x40 : num_frames;
            frame[0] = 0;
            frame[1] = block;
            frame[2] = step_frames;

            res = usb_bulk_write(handle, ep_out, frame, 3, 1000);
            assert(res == 3);

            res = usb_bulk_read(handle, ep_in, data, 3, 1000);
            assert(res == 3);
            fprintf(stderr, "Debug: %02hhx%02hhx%02hhx\n", data[0], data[1], data[2]);
            assert(data[0] == 2);

            int i;
            for(i=0; i < step_frames; ++i)
            {
                res = usb_bulk_read(handle, ep_in, data, 64, 5000);
                assert(res == 64);
                int take = (num >= 64) ? 64 : num;

                int j;
                for(j=0; j<take; j+=4)
                {
                    float temp = *((short int *) (data + j)) / 10.;
                    float hum = *((short int *) (data + j + 2)) / 10.;

                    time_t sampletime = timestamp + time_per_sample * sample;

                    fprintf(outfile, "%i %llu %g %g\n", sample,
                            (unsigned long long) sampletime,
                            temp, hum);
                    sample++;
                }
                num -= 64;
                num_frames -= 1;
            }
            ++block;
        }
        while(num_frames > 0);
    }
}

void
usage()
{
    fprintf(stderr, "pce v0.3 - PCE-HT71 programmer and data downloader\n");
    fprintf(stderr, "usage: pce [-n maxsamples] [-t timepersample] [-i] <-d downloadfile | -s>\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr, "  -i       Start measuring now, instead of waiting the manual button.\n");
    fprintf(stderr, "  -s       Program the capture according to '-n' and '-t'\n");
    fprintf(stderr, "  -d file  Download the data captured until now and stop it\n");
    fprintf(stderr, "  -n maxs  Set the maximum number of samples to capture (default 16000)\n");
    fprintf(stderr, "  -t time  Set the number of seconds between samples (default 2)\n");
    fprintf(stderr, "\nCopyright (C) 2010-2011  Lluis Batlle i Rossell\n");
    fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n");
    fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
    fprintf(stderr, "under certain conditions; read the COPYING file for details.");
}

int main(int argc, char **argv)
{
    int res;
    int opt;

    config.nsamples = 16000;
    config.immediate = 0;
    config.time_per_sample = 2;
    config.download_file = 0;
    action = HELP;
    while  ((opt = getopt(argc, argv, "n:d:t:si")) != -1) {
        switch (opt) {
            case 'n':
                config.nsamples = atoi(optarg);
                break;
            case 't':
                config.time_per_sample = atoi(optarg);
                break;
            case 'd':
                config.download_file = optarg;
                action = DOWNLOAD;
                break;
            case 's':
                action = SET;
                break;
            case 'i':
                config.immediate = 1;
                break;
            default:
                usage();
                return 1;
        }
    }

    if (action == HELP)
    {
        usage();
        return 0;
    }

    usb_init();

    usb_find_busses();
    usb_find_devices();
    
    connect();
    if (handle == 0)
    {
        fprintf(stderr, "Cannot find the PCE-HT71\n");
        return -1;
    }

    res = usb_claim_interface(handle,
        device->config->interface->altsetting->bInterfaceNumber);
    assert(res == 0 && "claim_interface");

    if (action == SET)
    {
        fprintf(stderr, "Settings: \n");
        fprintf(stderr, " Max samples: %i\n", config.nsamples);
        fprintf(stderr, " Time between samples: %i\n", config.time_per_sample);
        fprintf(stderr, " Mode: %s\n", config.immediate ? "immediate" : "manual");
        send_program();
        send_config();
    }
    else
    {
        outfile = fopen(config.download_file, "w");
        if (outfile != NULL)
        {
            start_download();
            fprintf(stderr, "File \"%s\" written\n", config.download_file);
            fclose(outfile);
        }
        else
        {
            fprintf(stderr, "Error opening file: %s", strerror(errno));
        }
    }

    res = usb_release_interface(handle,
        device->config->interface->altsetting->bInterfaceNumber);
    assert(res == 0 && "release_interface");
    usb_close(handle);
    handle = 0;
    return 0;
}