Mateusz Grzywacz / SDI12_device

SDI12_device.h

Committer:
amateusz
Date:
2018-08-06
Revision:
15:530bf2e09814
Parent:
13:0093792f2325
Child:
16:44daef8babd6

File content as of revision 15:530bf2e09814:

#include "SDI12.h"
#include <string>
#include "mbed.h"

class SDI12_device
{
public:
    struct Identification_struct {
        unsigned char sdi_version;
        char company[8+1];
        char model[6+1];
        char version[3+1];
    };

    struct Measurement_struct {
        unsigned long timestampMeasurementReadyAt;
        char readyIn;
        bool _measurementAlreadyRead;
        char count;
        float * values;  // not float values [] !! :O
    };

    Identification_struct _ident_struct;

    static int detect(SDI12 &sdi12, char indices[], int stop = -1, int start = -1) {
        char found = 0;
        for (size_t i = (start==-1?0:start); i < (stop==-1?ADDRRICES.size():stop+1); ++i) {
            string question = "?!";
            question[0] = ADDRRICES[i];
            sdi12.sendCommand(question);
            std::string response;
            Timer detectTimer;
            detectTimer.start();
            while (detectTimer.read_ms() < RESPONSE_TIMEOUT * 4) {
                if (sdi12.RxInProgress()) detectTimer.reset();
                if (sdi12.RxBufferAvailable()) {
                    unsigned char bufferSize = sdi12.RxBufferAvailable();
                    char buffer[bufferSize];
                    sdi12.getRxBuffer(buffer);
                    // if first char is valid address char
                    if (ADDRRICES.find(buffer[0])) {
                        indices[found++] = buffer[0];
                        debug("FOUND: %d -> %c\r\n", i, indices[found-1]);
                    }
                }
            }
            osDelay(100);
        }
        return found;
    };
    
    SDI12_device(){
        debug("DEFAULT\r\n");
        }
    
    SDI12_device (SDI12 *inst, char address, EventQueue &mainDispatcher) : _sdi12(inst) {
        sensorQueue = &EventQueue(4*EVENTS_QUEUE_SIZE);
        setAddress(address);
        parentQueue = &mainDispatcher;
        sensorQueue->chain(parentQueue);
        if(getIdentification(_ident_struct));
    };

    SDI12_device (SDI12 *inst, char address) : _sdi12(inst) {
        sensorQueue = NULL;
        setAddress(address);
        parentQueue = NULL;
        if(getIdentification(_ident_struct));
    };


    SDI12_device & operator= (const SDI12_device & old) {
        _address = old._address;
        _ident_struct = old._ident_struct;
//        _sdi12 = old._sdi12; // non-copyable -.-

        _measurementEventId = old._measurementEventId;
        _measurement = old._measurement;
        _valuesReadyCount = old._valuesReadyCount;
        _measurementReady = old._measurementReady;

        parentQueue = old.parentQueue;
        sensorQueue = old.sensorQueue;
    }

    ~SDI12_device () {
        delete [] _measurement.values;
        // unchain event queue
//        sensorQueue.cancel(_measurementEventId);
        if (sensorQueue != NULL) sensorQueue->chain(NULL);
        
    }

    // concurrent means that device under measurement won't signal its prempt readiness to the bus
    int measure(bool concurrent = true, char group = '0') {

        std::string question = string("?") + string(concurrent?"C":"M") + string("!");
        question[0] = _address;
        if (group != '0')
            question.insert(2, 1, group); // e.g. 2M! -> 2M1!
        _sdi12->sendCommand(question);

        Timer timeout;
        timeout.start();
        while (sdi12.RxBufferAvailable() == 0) {
            if (sdi12.RxInProgress()) timeout.reset();
            if(timeout.read_ms() > RESPONSE_TIMEOUT) {
                return false;
            }
        }

        unsigned char bufferSize = sdi12.RxBufferAvailable();
        char buffer[bufferSize+1];
        sdi12.getRxBuffer(buffer);
        buffer[bufferSize] = '\0';
        std::string response(buffer);

        if (response[0] == _address) {
            char measurementsCount = std::atoi(response.substr(1+3,(concurrent?2:1)).c_str());
            char waitFor = std::atoi(response.substr(1,3).c_str());
            _measurement.count = measurementsCount;
            _measurement.values = new float[measurementsCount];
            _measurement.readyIn = waitFor;
//            _measurement.timestampMeasurementReadyAt
            debug("wait for %d measurement for %d seconds...", _measurement.count, waitFor);
            _valuesReadyCount = 0;
            _measurementReady = false;
            if (parentQueue != NULL)
                _measurementEventId = sensorQueue->call_in(waitFor * 1000 + 500, callback(this, &SDI12_device::read), '0'); // + some [ms] for good measure, not to be too early
//            debug("measurement scheduled");
//            if (_measurement.values != NULL) delete[] _measurement.values;
//            _measurement.values = new float[measurementsCount];
            return 0;
        }
        return -1;
    };

    char readyIn() {
        return _measurement.readyIn;
    }

    bool isReady() {
        return _measurementReady;
    }

    bool read(char group = '0') {
//         Measurement (M), Continuous (R), and Concurrent (C) commands and subsequent Data (D)
        std::string question = std::string("?") + std::string("D") + std::string(1,group)+ std::string("!");
        question[0] = _address;
        _sdi12->sendCommand(question);

        Timer timeout;
        timeout.start();
        while (sdi12.RxBufferAvailable() == 0) {
            if (sdi12.RxInProgress()) timeout.reset();
            if(timeout.read_ms() > RESPONSE_TIMEOUT) {
                return false;
            }
        }
        unsigned char bufferSize = sdi12.RxBufferAvailable();
        char buffer[bufferSize+1];
        sdi12.getRxBuffer(buffer);
        buffer[bufferSize] = '\0';
        std::string response(buffer);

//        debug("parser. recv: %s\r\n", response);
        if (response[0] == _address) {
            debug("\r\n--- %s ---\r\n", response);
            // parser here
            response = response.substr(1); // to limit repeting this operation later. i.e. extract only values e.g. +21.3-123+123
            // the only two possible delimeters of a value
            size_t next_start_index = response.find_first_of("-+", 0);
            size_t next_end_index;
            // ready, steady, parse!!
            while(next_start_index != std::string::npos) {
                // determine start index:
                next_end_index = response.find_first_of("-+", next_start_index + 1); // std::substr is prepared to take std::npos
                
                float value = std::atof(response.substr(next_start_index, next_end_index).c_str());
//                debug("parsed: %f\r\n", value);
                _measurement.values[_valuesReadyCount++] = value;
                next_start_index = response.find_first_of("-+", next_end_index);
            }
            // if current parsing doesn't return all the expexted measurements, then press harder and poll further Dx commands. RECURSION HERE *.*
            if (_valuesReadyCount < _measurement.count)
                read(group + 1);
            else {
                _measurementReady = true;
            }
        }
        return true;
    }


//
    const unsigned char getMeasurementCount() {
        return _measurement.count;
    }

    const float getMeasurementValue(char which) {
        _measurementReady = false; // reading /any/ of the values resets this
        return _measurement.values[which];
    }

    void setAddress(char address) {
        _address = address;
    };
//
//
private:
    static const int RESPONSE_TIMEOUT = 15+1; // device should start to respond within [ms]
    static const std::string ADDRRICES; // as in index -> indices,

    SDI12 *_sdi12;
    char _address;
    int _measurementEventId;
    Measurement_struct _measurement;
    size_t _valuesReadyCount;
    bool _measurementReady;

    EventQueue *parentQueue;
    EventQueue *sensorQueue;

    bool getIdentification(Identification_struct &ident) {
//        _sdi12->sendCommand(std::string(_address) + "I!");
        std::string question = "?I!";
        question[0] = _address;
        _sdi12->sendCommand(question);

        Timer timeout;
        timeout.start();
        while (sdi12.RxBufferAvailable() == 0) {
            if (sdi12.RxInProgress()) timeout.reset();
            if(timeout.read_ms() > RESPONSE_TIMEOUT) {
                return false;
            }
        }
        unsigned char bufferSize = sdi12.RxBufferAvailable();
        char buffer[bufferSize+1];
        sdi12.getRxBuffer(buffer);
        buffer[bufferSize] = '\0';
        std::string response(buffer);

        if (response[0] == _address) {
            // e.g.: 113DECAGON GS3   402

            ident.sdi_version = std::atoi(response.substr(1, 2).c_str());
            std::string tempStr = response.substr(3, 8);
            strcpy(ident.company, tempStr.c_str());

            tempStr = response.substr(3+8, 6);
            strcpy(ident.model, tempStr.c_str());

            tempStr = response.substr(3+8+6, 3);
            strcpy(ident.version, tempStr.c_str());
            return true;
        }
        return false;
    }

};

const std::string SDI12_device::ADDRRICES = "0123456789abcdefgijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";