Reading Utility metering data using MBED

Dependencies:   mbed

mt382.h

Committer:
pwheels
Date:
2014-05-16
Revision:
4:1ebd1aabaa16
Parent:
2:b386810af678

File content as of revision 4:1ebd1aabaa16:

#ifndef MBED_H
#include "mbed.h"
#endif
/*
 * Copyright (c) 2012 Paul van der Wielen, Pro-Serv
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to use
 * and implement the software for none commercial reason and usage only and
 * 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.
 *
 * Usage and assumptions:
 * interfacing an MT382 smart meter using P1 port, schematic is fairly simple and
 * provides for an isolated connection to the outside world, schematic to foloow in due time.
 *
 * Overview of used record codes & formats
 *  0-0:96.1.1(5A424556303035303936303530313132)        Serialno meter in hexadecimal ascii code
 *  1-0:1.8.1(00185.000*kWh)                          * Total consumpion tarif 1 (night)
 *  1-0:1.8.2(00084.000*kWh)                          * Total consumpion tarif 2 (day)
 *  1-0:2.8.1(00013.000*kWh)                          * Total delivery tarif 1 (night)
 *  1-0:2.8.2(00019.000*kWh)                          * Total delivery tarif 2 (day)
 *  0-0:96.14.0(0001)                                 1 Actual tarif (1)
 *  1-0:1.7.0(0000.98*kW)                             * Current usage
 *  1-0:2.7.0(0000.00*kW)                             * Current delivery
 *  0-0:17.0.0(999*A)                                   Maximum current by phase
 *  0-0:96.3.10(1)                                      Position of switch
 *  0-0:96.13.1()                                     1 Message (numeric)
 *  0-0:96.13.0()                                     1 Message (text)
 *  0-1:24.1.0(3)                                       Other equipment on M-Bus
 *  0-1:96.1.0(3238313031353431323030333330313132)      Serialno of gasmeter
 *  0-1:24.3.0(120517020000)(08)(60)(1)(0-1:24.2.1)(m3) Time of last gas measurement (120517020000 = 17 mei 2012 2uur
 *  (00124.477)                                       * Total consumption gas
 *  0-1:24.4.0(1)                                       Position of gas valve?
 *                                                    *-Implemented, 1-planned for later date
 */
 
namespace MT {

class MT382 {

protected:
    DigitalOut  *_out;
    Timeout     *_delay;
    Serial      *_dev;
    float       _defDelay;
    int         _defBaud;
    int         _defBits;
    int         _defParity;
    int         _defStop;
    // array with items to find in data stream
    char        _l_mt[7][8];
    char        _mt_temp[32];
    char        _mt_line[64];
    char        _mt_data[512];
    double      _mt_res[7];
    // stream status (1 = start char , 2 = stop char)
    int         _mt_sts;
    int         _mt_pnt;
    int         _mt_flg;
    
    void init(PinName po, PinName tx, PinName rx) {
    _out = new DigitalOut( po );
    _out->write(1);
    _dev = new Serial(tx, rx);
    _delay = new Timeout;
    _defBaud = 9600;
    _defBits = 7;
    _defParity = 2;
    _defStop = 1;
    setComm();
    }
    
public:

    friend class Timeout;

    /* PinDetect constructor(s) & overloading */
    MT382() { error("Provide valid PinName for out, tx & rx"); }

    MT382(PinName po, PinName tx, PinName rx) {
        init( po, tx, rx);
    }

    /* PinDetect destructor */    
    ~MT382() {
        if ( _delay )  delete( _delay );
        if ( _out )    delete( _out );
        if ( _dev )    delete( _dev );
    }

    // adjust communication parameters as needed
    void setComm(int baud = 9600, int bits = 7, int parity = 2, int stop = 1) {
        _defBaud = baud;
        _dev->baud(_defBaud);
        _defBits = bits;
        _defParity = parity;
        _defStop = stop;
        
        switch (_defParity) {
            case 0:
                _dev->format(_defBits, Serial::None, _defStop);
                break;
            case 1:
                _dev->format(_defBits, Serial::Odd, _defStop);
                break;
            case 2:
                _dev->format(_defBits, Serial::Even, _defStop);
                break;
            default:
                _dev->format(_defBits, Serial::None, _defStop);
                break;
        }
    }

    // adjust timeout value
    void setTime(float maxtime = 5.0) {
        _defDelay = maxtime;
    }
    
    // adjust the value match strings
    void setMatch(char * m0 = "", char * m1 = "", char * m2 = "",
         char * m3 = "", char * m4 = "", char * m5 = "", char * m6 = "") {
        strcpy (_l_mt[0],m0);
        strcpy (_l_mt[1],m1);
        strcpy (_l_mt[2],m2);
        strcpy (_l_mt[3],m3);
        strcpy (_l_mt[4],m4);
        strcpy (_l_mt[5],m5);
        strcpy (_l_mt[6],m6);
    }

    // signal time out condition
    void isr_delay() {
        _mt_sts = 0x80;
        _delay->detach();
    }
    
    // get data from 'smart meter'
    int getReading () {

        _delay->attach(this, &MT382::isr_delay, _defDelay);
        _out->write(0);
        _mt_pnt = 0;
        _mt_sts = 0;
        
        while((_mt_sts & 0x02) == 0 && (_mt_sts & 0x80) == 0) {
            if(_dev->readable()) {
                // get available character from MT382
                char mt_tst = _dev->getc();
                // remove any ascii NULL character
                if (mt_tst != 0x00) {
                    _mt_data[_mt_pnt++] = mt_tst;
                    if (_mt_data[_mt_pnt-1] == '/') {
                        _mt_sts = 1;
                    }
                    if (_mt_data[_mt_pnt-1] == '!') {
                        _mt_sts = _mt_sts | 0x02;
                        _delay->detach();
                        _out->write(1);
                    }
                }
            }
        }
        
        int mt_tmp = 0;
        _mt_flg = 0;
        int mt_elem = sizeof(_l_mt)/sizeof(_l_mt[0]);
        if ((_mt_sts == 0x03) && ((_mt_sts & 0x80) == 0)){
            for (int buf_pnt = 0; buf_pnt <= _mt_pnt; buf_pnt++) {
                _mt_line[mt_tmp++] = _mt_data[buf_pnt];
                if (_mt_data[buf_pnt] == 0x0a) {
                    _mt_line[mt_tmp] = 0x00;
                    if ((_mt_flg & 0x01) == 0x01) {
                        // special case handling -> param 5
                        strncpy (_mt_temp, strchr(_mt_line,'(') + 1, strchr(_mt_line,')') - strchr(_mt_line,'(') - 1);
                        _mt_res[(_mt_flg >> 4) & 0x0f] = strtod(_mt_temp, NULL);
                        _mt_flg = 0;
                    }
                    for (int j = 0; j < mt_elem; j++) {
                        if (strstr (_mt_line, _l_mt[j])) {
                            for (int cnt = 0; cnt < sizeof(_mt_temp); cnt++) {
                                _mt_temp[cnt] = 0x00;
                            }
                            if (j < 6) {
                                strncpy (_mt_temp, strchr(_mt_line,'(') + 1, strchr(_mt_line,'*') - strchr(_mt_line,'(') - 1);
                            } else {
                                // flag special case due to inconsistent formating of raw data
                                _mt_flg = j << 4 | 0x01;
                            }
                            _mt_res[j] = strtod(_mt_temp, NULL);
                        }
                    }
                mt_tmp = 0;
                }
            }
        } else {
            return -1;
        }
        return 0;
    }

    // return raw data character count
    int getCount(void) {
        return _mt_pnt;
    }

    // return selected value
    double getValue(int param = 0) { 
        return _mt_res[param];
    }
    
    // return raw data 
    char * getRaw(void) {
        return _mt_data;
    }
    
    // return pointer to array with collected data
    double * getArray(void) { 
        return _mt_res;
    }   
};

};                                                              // namespace MT ends.

using namespace MT;