DMX512, RDM send/recv library http://mbed.org/users/okini3939/notebook/dmx512

Dependents:   dmx_test ArtNodeLED SPK-DVIMXR SPK-DMXer ... more

DMX512 send/recv library

DMX512 is protocol for lighting.

調光プロトコル DMX512 を送受信するライブラリです。

see: http://mbed.org/users/okini3939/notebook/dmx512/

LPC1114 support is thanks to Stanly Chen

RDM.cpp

Committer:
okini3939
Date:
2017-10-20
Revision:
19:ae8fd2ba7c53

File content as of revision 19:ae8fd2ba7c53:

/*
 * DMX512, RDM send/recv library
 * Copyright (c) 2017 Hiroshi Suga
 * Released under the MIT License: http://mbed.org/license/mit
 */

/** @file
 * @brief DMX512 send/recv
 */

#include "mbed.h"
#include "DMX.h"

#ifdef RDM_ENABLE

#define htons(n) __REV16(n)
#define ntohs(n) __REV16(n)
#define htonl(n) __REV(n)
#define ntohl(n) __REV(n)

static unsigned char uid_broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

void DMX::pollRdm () {
    int i, len, crc, forMe, forGrp, forAll;
    struct RDM_DATA *rdm = (struct RDM_DATA *)data_rx;

    if (!is_rdm_received) return;
    is_rdm_received = 0;
    if (rdm->Length < 24) return;

    len = rdm->DataLength;
    crc = (rdm->Data[len] << 8) | rdm->Data[len + 1];
    if (calcCrc(data_rx, rdm->Length - 1) != crc) return;

    forMe  = memcmp(rdm_uid, rdm->DestID, 6) == 0 ? 1 : 0;
    forGrp = (rdm_uid[0] == rdm->DestID[0] && rdm_uid[1] == rdm->DestID[1] && memcmp(&rdm_uid[2], uid_broadcast, 4) == 0) ? 1 : 0;
    forAll = memcmp(rdm_uid, uid_broadcast, 6) == 0 ? 1 : 0;

    switch (rdm->CmdClass) {
    case E120_DISCOVERY_COMMAND:
        switch (ntohs(rdm->Parameter)) {
        case E120_DISC_UNIQUE_BRANCH:
            if (rdm->DataLength == 12 && !rdm_mute) {
                if (memcmp(rdm_uid, &rdm->Data[0], 6) >= 0 && memcmp(rdm_uid, &rdm->Data[6], 6) <= 0) {
                    sendRdmDiscResponse(rdm);
                }
            }
            break;

        case E120_DISC_MUTE:
        case E120_DISC_UN_MUTE:
            if (forMe || forGrp || forAll) {
                if (rdm->CmdClass == E120_DISC_MUTE) {
                    rdm_mute = 1;
                } else {
                    rdm_mute = 0;
                }
                if (forMe) sendRdmDiscMuteResponse(rdm);
            }
            break;

        }
        break;

    case E120_DISCOVERY_COMMAND_RESPONSE:
        switch (ntohs(rdm->Parameter)) {
        case E120_DISC_MUTE:
        case E120_DISC_UN_MUTE:
            if (forMe && memcmp(found_uid, rdm->SourceID, 6) == 0) {
                if (buf_uid_size && buf_uid_count < buf_uid_size) {
                    memcpy(&buf_uid[6 * buf_uid_count], rdm->SourceID, 6);
                    buf_uid_count ++;
                }
            }
        }
        break;

    case E120_GET_COMMAND:
    case E120_SET_COMMAND:
        if ((forMe || forGrp || forAll) && cb_RdmParser) {
            cb_RdmParser(rdm);
        }
        break;

    case E120_GET_COMMAND_RESPONSE:
    case E120_SET_COMMAND_RESPONSE:
        if ((forMe || forGrp || forAll) && cb_RdmParser) {
            cb_RdmParser(rdm);
        }
        break;
    }
}

int DMX::calcCrc (unsigned char *buf, int len, int offset) {
    int i, crc = offset;

    for (i = 0; i < len; i ++) {
        crc += buf[i];
    }
    return crc;
}

void DMX::rdmStart (int block) {
    Timer t;

    t.start();
    while (mode_rx != DMX_MODE_BEGIN && t.read_ms() < 100) {
        pollRdm();
    }
    t.stop();

    mode_rdm = 1;
    mode_rx = DMX_MODE_BEGIN;
    mode_tx = DMX_MODE_BEGIN;
    is_sent = 0;
    is_received = 0;
    is_rdm_received = 0;
    timeout01.attach_us(this, &DMX::int_timer, RDM_TIME_DELAY);

    if (block) {
        while (mode_rdm);
    }
}

void DMX::rdmWaitResponse (int ms) {
    Timer t;

    t.start();
    while (t.read_ms() < ms && !is_rdm_received) {
        pollRdm();
    }
    t.stop();
    pollRdm();
}

int DMX::sendRdmDiscResponse (struct RDM_DATA *rdm) {
    int crc;
    unsigned char data[40];

    data[0]  = 0xFE;
    data[1]  = 0xFE;
    data[2]  = 0xFE;
    data[3]  = 0xFE;
    data[4]  = 0xFE;
    data[5]  = 0xFE;
    data[6]  = 0xFE;
    data[7]  = 0xAA;
    data[8]  = rdm_uid[0] | 0xAA;
    data[9]  = rdm_uid[0] | 0x55;
    data[10] = rdm_uid[1] | 0xAA;
    data[11] = rdm_uid[1] | 0x55;
    data[12] = rdm_uid[2] | 0xAA;
    data[13] = rdm_uid[2] | 0x55;
    data[14] = rdm_uid[3] | 0xAA;
    data[15] = rdm_uid[3] | 0x55;
    data[16] = rdm_uid[4] | 0xAA;
    data[17] = rdm_uid[4] | 0x55;
    data[18] = rdm_uid[5] | 0xAA;
    data[19] = rdm_uid[5] | 0x55;
    crc = calcCrc(&data[8], 12, 0);
    data[20] = ((crc >> 8) & 0xFF) | 0xAA;
    data[21] = ((crc >> 8) & 0xFF) | 0x55;
    data[22] = (crc & 0xFF) | 0xAA;
    data[23] = (crc & 0xFF) | 0x55;

    wait_us(RDM_TIME_DELAY);
/*
    do {
        wait_us(RDM_TIME_DELAY * (rand() / (RAND_MAX / 10)));
    } while (mode_rx != DMX_MODE_BEGIN);
*/
    if (_xmit) _xmit->write(XMIT_TX);
    wait_us(10);
    for (int i = 0; i < 24; i ++) {
        _dmx.putc(data[i]);
    }
    while (!(_uart->LSR & (1<<6))); // TEMT
    if (_xmit) _xmit->write(XMIT_RX);

    return 0;
}

int DMX::sendRdmMsg (struct RDM_DATA *rdm, int CmdClass, unsigned char *data, int len) {
    int crc;
    struct RDM_DATA *rdm_msg = (struct RDM_DATA *)data_rdm;

    while (mode_rdm);
    rdm_msgcount ++;
    rdm_msg->SubStartCode   = E120_SC_SUB_MESSAGE;
    rdm_msg->Length         = 24 + len;
    memcpy(rdm_msg->DestID, rdm->SourceID, 6);
    memcpy(rdm_msg->SourceID, rdm_uid, 6);
    rdm_msg->TransactionNo  = rdm->TransactionNo;
    rdm_msg->ResponseType   = E120_RESPONSE_TYPE_ACK;
    rdm_msg->MessageCount   = rdm_msgcount;
    rdm_msg->SubDev         = 0;
    rdm_msg->CmdClass       = CmdClass;
    rdm_msg->Parameter      = rdm->Parameter;
    rdm_msg->DataLength     = len;
    if (data && len) {
        memcpy(rdm_msg->Data, data, len);
    }
    crc = calcCrc((unsigned char *)rdm_msg, rdm_msg->Length - 1);
    rdm_msg->Data[len]      = (crc >> 8) & 0xFF;
    rdm_msg->Data[len + 1]  = crc & 0xFF;

    rdmStart();
    return 0;
}

int DMX::sendRdmMsg (unsigned char *dest, int CmdClass, int Parameter, int Type, unsigned char *data, int len, int block) {
    int crc;
    struct RDM_DATA *rdm_msg = (struct RDM_DATA *)data_rdm;

    while (mode_rdm);
    rdm_transno ++;
    rdm_msgcount ++;
    rdm_msg->SubStartCode   = E120_SC_SUB_MESSAGE;
    rdm_msg->Length         = 24 + len;
    memcpy(rdm_msg->DestID, dest, 6);
    memcpy(rdm_msg->SourceID, rdm_uid, 6);
    rdm_msg->TransactionNo  = rdm_transno;
    rdm_msg->ResponseType   = Type;
    rdm_msg->MessageCount   = rdm_msgcount;
    rdm_msg->SubDev         = 0;
    rdm_msg->CmdClass       = CmdClass;
    rdm_msg->Parameter      = htons(Parameter);
    rdm_msg->DataLength     = len;
    if (data && len) {
        memcpy(rdm_msg->Data, data, len);
    }
    crc = calcCrc((unsigned char *)rdm_msg, rdm_msg->Length - 1);
    rdm_msg->Data[len]      = (crc >> 8) & 0xFF;
    rdm_msg->Data[len + 1]  = crc & 0xFF;

    rdmStart(block);
    return 0;
}

int DMX::sendRdmDiscMuteResponse (struct RDM_DATA *rdm) {
    unsigned char data[2] = {0, 0};

    return sendRdmMsg(rdm, E120_DISCOVERY_COMMAND_RESPONSE, data, 2);
}

void DMX::attachRdmCallback (void (*handler)(struct RDM_DATA *), char *uid) {

    cb_RdmParser = handler;
    memcpy(rdm_uid, uid, 6);
}

void DMX::int_rdm () {
    int flg, dat;

    flg = _uart->LSR;
    dat = _dmx.getc();

    if (flg & ((1 << 7)|(1 << 3)|(1 << 4))) {
        // Conflict
        addr_rx = -1;
        return;
    }

    if (addr_rx >= 0 && addr_rx < DMX_SIZE) {
        data_rx[addr_rx] = dat;
        addr_rx ++;
    }
}

int DMX::sendRdmDiscMute (unsigned char *dest, int mute) {

    while (mode_rdm);

    int param = mute ? E120_DISC_MUTE : E120_DISC_UN_MUTE;
    sendRdmMsg(dest, E120_DISCOVERY_COMMAND, param ,E120_RESPONSE_TYPE_ACK_TIMER, NULL, 0, 1);
    rdmWaitResponse(100);
    return 0;
}

int DMX::sendRdmDiscovery (uint64_t uid_begin, uint64_t uid_end) {
    int crc;
    unsigned char data[12];

    while (mode_rdm);

    data[ 0] = (uid_begin >> 40) & 0xff;
    data[ 1] = (uid_begin >> 32) & 0xff;
    data[ 2] = (uid_begin >> 24) & 0xff;
    data[ 3] = (uid_begin >> 16) & 0xff;
    data[ 4] = (uid_begin >> 8) & 0xff;
    data[ 5] = uid_begin & 0xff;
    data[ 6] = (uid_end >> 40) & 0xff;
    data[ 7] = (uid_end >> 32) & 0xff;
    data[ 8] = (uid_end >> 24) & 0xff;
    data[ 9] = (uid_end >> 16) & 0xff;
    data[10] = (uid_end >> 8) & 0xff;
    data[11] = uid_end & 0xff;

    _dmx.attach(this, &DMX::int_rdm, Serial::RxIrq);
    addr_rx = 0;
    sendRdmMsg(uid_broadcast, E120_DISCOVERY_COMMAND, E120_DISC_UNIQUE_BRANCH ,E120_RESPONSE_TYPE_ACK_TIMER, data, 12, 1);
    rdmWaitResponse(100);
    _dmx.attach(this, &DMX::int_rx, Serial::RxIrq);

    if (addr_rx >= 24) {
        if (data_rx[0] == 0xfe && data_rx[7] == 0xaa) {
            crc = calcCrc(&data_rx[8], 12, 0);
            if (data_rx[20] == (((crc >> 8) & 0xFF) | 0xAA) &&
              data_rx[21] == (((crc >> 8) & 0xFF) | 0x55) &&
              data_rx[22] == ((crc & 0xFF) | 0xAA) && data_rx[23] == ((crc & 0xFF) | 0x55)) {
                found_uid[0] = (data_rx[ 8] & 0x55) | (data_rx[ 9] & 0xAA);
                found_uid[1] = (data_rx[10] & 0x55) | (data_rx[11] & 0xAA);
                found_uid[2] = (data_rx[12] & 0x55) | (data_rx[13] & 0xAA);
                found_uid[3] = (data_rx[14] & 0x55) | (data_rx[15] & 0xAA);
                found_uid[4] = (data_rx[16] & 0x55) | (data_rx[17] & 0xAA);
                found_uid[5] = (data_rx[18] & 0x55) | (data_rx[19] & 0xAA);
                return 1;
            }
            return -1;
        }
        return -1;
    } else
    if (addr_rx) {
        return -1;
    }

    return 0;
}

int DMX::rdmDiscoverySub (uint64_t uid_begin, uint64_t uid_end, int ttl) {
    int r;
    uint64_t uid_mid;

//    printf("rdmDiscoverySub %04x %08x - %04x %08x\r\n", (int)(uid_begin>>32), (int)uid_begin, (int)(uid_end>>32), (int)uid_end);
    if (!ttl) return 0;
    ttl --;

    r = sendRdmDiscovery(uid_begin, uid_end);
    if (uid_begin >= uid_end) {
        if (r > 0) {
            sendRdmDiscMute(found_uid, 1);
        }
    } else {
        if (r > 0) {
            sendRdmDiscMute(found_uid, 1);
        }
        if (r) {
            uid_mid = (uid_begin + uid_end) / 2;
            r = rdmDiscoverySub(uid_begin, uid_mid, ttl);
            r |= rdmDiscoverySub(uid_mid + 1, uid_end, ttl);
        }
    }
    return r;
}

int DMX::rdmDiscovery (unsigned char *buf, int size) {

    buf_uid_count = 0;
    buf_uid_size = size;
    buf_uid = buf;
    sendRdmDiscMute(uid_broadcast, 0);
    rdmDiscoverySub(0x000000000000, 0xfffffffffffe, 10);
    return buf_uid_count;
}

#endif