http://mbed.org/users/okini3939/notebook/art-net/

Dependents:   ArtNode ArtNode DMXStation ArtNodeLED ... more

DmxArtNet.cpp

Committer:
okini3939
Date:
2019-09-13
Revision:
2:0753f1ed1dec
Parent:
1:c59dc374fc64
Child:
3:89aa639c946a

File content as of revision 2:0753f1ed1dec:

/*
 * Control Art-Net from freepascal & delphi
 * (c) Rowan Maclachlan (hippy) rowanmac@optusnet.com.au [15d/01m/06y]
 *
 * Free for personal not-for-profit use only, please contact me if you are
 * using it in a commercial product, as i would like a copy :)
 *
 * http://members.westnet.com.au/rowanmac/
 *
 * for mbed ported by Suga 2011, 2017
 */

/** @file
 */

#include "mbed.h"
#include "EthernetInterface.h"
#include "DmxArtNet.h"
#include <stdio.h>
#include <string.h>

//#define DEBUG
#ifdef DEBUG 
#define DBG(...) printf("" __VA_ARGS__) 
#else 
#define DBG(...) 
#endif 

/*
// host to network short
#define htons( x ) ( (( (x) << 8 ) & 0xFF00) | (( (x) >> 8 ) & 0x00FF) )
#define ntohs( x ) htons(x)
// host to network long
#define htonl( x ) ( (( (x) << 24 ) & 0xFF000000)  \
                   | (( (x) <<  8 ) & 0x00FF0000)  \
                   | (( (x) >>  8 ) & 0x0000FF00)  \
                   | (( (x) >> 24 ) & 0x000000FF)  )
#define ntohl( x ) htonl(x)
*/

extern "C" void mbed_mac_address(char *s);


DmxArtNet::DmxArtNet () {
    BindIpAddress[0] = 0;
    BCastAddress[0] = 0;
    Init_ArtDMX();
    InitArtPollReplyDefaults();
}

// function to make a word (16bit) from two bytes (2 x 8bit)
int DmxArtNet::makeword16 (int lsb, int msb) {
    return (msb << 8) + lsb;
}

// report a socket error, halt program
void DmxArtNet::SocketErrorOccured (char *proc) {
    LError = 1;
    snprintf(LErrorString, sizeof(LErrorString), "socket> error occured in '%s'\r\n", proc);
    DBG("%s", LErrorString);
}

// clear error
void DmxArtNet::ClearError () {
   LError = 0;
}

int DmxArtNet::Init () {
   int i;
   int err;

   rxlen = 0;
   cb_ArtParser = NULL;
   LError = 0; // no error yet :)
   LastRecievedUniverse = 0;

//   Init_ArtDMX(); // initialize ArtDmx structure

   if (BindIpAddress[0] == 0) {
       char mac[6];
       mbed_mac_address(mac);
       sprintf(BindIpAddress, "%d.%d.%d.%d", 2, mac[3], mac[4], mac[5]);
       strcpy(SubNetMask, "255.0.0.0");
   }
   if (BCastAddress[0] == 0) {
       bcast(BCastAddress, BindIpAddress, SubNetMask);
   }
   RemoteSin.set_address(BCastAddress, ArtUDPPort);

   err = _art.bind(ArtUDPPort);
   if (err) {
       SocketErrorOccured("Bind error");
       return -1;
   }
//   _art.set_broadcasting(true);

    int ip[4];
    sscanf(BindIpAddress, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
    for (i = 0; i < 4; i ++) {
        localaddr.IP[i] = ip[i];
    }
    localaddr.Port = ArtUDPPort;
    ArtPollReply.Addr = localaddr;

    for (i = 0; i < ArtMaxUniv; i ++) {
//        DmxIn[i] = new unsigned char[DMX_SIZE];
        DmxIn[i] = (unsigned char*)malloc(DMX_SIZE);
        memset(DmxIn[i], 0, DMX_SIZE);
    }

    thread = new Thread(&DmxArtNet::task_UDPSocket, (void*)this, osPriorityNormal, 1024*4);

   return 0;
}

void DmxArtNet::task_UDPSocket (void const *arg) {
    DmxArtNet *inst = (DmxArtNet*)arg;

    inst->on_UDPSocketEvent();
}

void DmxArtNet::on_UDPSocketEvent () {
    int n;

    for (;;) {
        Work();

        n = _art.receiveFrom(RemoteSin, (char*)buf, sizeof(buf));
        if (n < 0) break;
        if (n == 0) continue;
        rxlen = n;
    }
}

int DmxArtNet::Work() {
   int i, n;
   struct ArtPacketHeader *ArtHead = (ArtPacketHeader*)buf;

   if (rxlen >= sizeof(ArtPacketHeader)) {
      // retreive the packet header

     // if looback disabled
     if (strcmp(RemoteSin.get_address(), BindIpAddress) == NULL) {
         DBG("ArtNet> loopback detected\r\n");

         rxlen = 0;
         return 0; // don't read packets
                   // which we sent!
     }

     // confirm is vaild art-net packet
     if (strncmp(ArtHead->ID, ArtHeaderID, 8) != 0) {
         rxlen = 0;
         return 0;
     }

     DBG("ArtNet> RX: %d OpCode: %04x\r\n", rxlen, ArtHead->OpCode);

       switch (ArtHead->OpCode) { // determine packet type

        case OP_Output:  // 0x5000  An ArtDMX Packet, containts dmx universe
            if (rxlen >= sizeof(struct ArtDMX_Packet) - (DMX_SIZE - 2)) {
                struct ArtDMX_Packet *artdmx = (struct ArtDMX_Packet*)buf;
            
                // check data length
                if (ntohs(artdmx->Length) > DMX_SIZE || (artdmx->Universes & 0x7ff0) != (NetSwitch & 0x7ff0)) {
                    break;
                }

                for (i = 0; i < ArtMaxUniv; i ++) {
                  if ( (ArtMaxUniv <= 4 && ArtPollReply.Swout[i] == (artdmx->Universes & 0x0f)) ||
                   (ArtMaxUniv > 4 && ArtPorts.Swout[i] == (artdmx->Universes & 0x0f)) ) {
                    LastRecievedUniverse |= (1<<i);
                    // get the dmx data
                    memcpy(DmxIn[i], artdmx->Data, ntohs(artdmx->Length));
                    DBG(" <Art-Dmx> Univ: %d  Length: %d  Ch1: %d\r\n", artdmx->Universes, ntohs(artdmx->Length), DmxIn[i][0]);
                    DBG(" from %s\r\n", RemoteSin.get_address());
                    rxlen = 0;
                    return 1; // something happened!
                  }
                }
            }
            break; // OP_Output

        case OP_Poll:  // 0x2000
            if (rxlen == sizeof(struct ArtPoll_Packet)) {
                struct ArtPoll_Packet *artpoll = (struct ArtPoll_Packet*)buf;

                if (!(artpoll->TalkToMe & 1)) {
                    RemoteSin.set_address(BCastAddress, ArtUDPPort);
                }
                DBG(" <Art-Poll> Ver: %d TalkToMe: %d\r\n", ArtPoll.Version, ArtPoll.TalkToMe);
                SendArtPollReply(); // send a reply to the ArtPoll
            }
            break; // OP_Poll

        case OP_PollReply:  // 0x2100
            {
                struct ArtPollReply_Packet *pollreply = (struct ArtPollReply_Packet*)buf;

                DBG(" <Art-PollReply> Startus: %02x Name: %s\r\n", pollreply->Status, pollreply->ShortName);
            }
            break; // OP_PollReply

        case OP_Address:  // 0x6000
            if (rxlen == sizeof(struct ArtAddress_Packet)) {
                struct ArtAddress_Packet *address = (struct ArtAddress_Packet *)buf;

                if ((address->NetSwitch & 0x80) && (address->SubSwitch & 0x80)) {
                    NetSwitch = ((address->NetSwitch & 0x7f) << 8) | ((address->SubSwitch & 0x0f) << 4);
                    ArtPollReply.NetSwitch = (NetSwitch >> 8) & 0x7f;
                    ArtPollReply.SubSwitch  = (NetSwitch >> 4) & 0x0f;
                }
                if (address->ShortName[0]) {
                    strncpy(ArtPollReply.ShortName, address->ShortName, 18);
                }
                if (address->LongName[0]) {
                    strncpy(ArtPollReply.LongName, address->LongName, 64);
                }
                if (address->BindIndex) {
                    n = (address->BindIndex - 1) * 4;
                } else {
                    n = 0;
                }
                for (i = 0; i < 4; i ++) {
                    if (address->Swin[i] != 0x7f) {
                        ArtPollReply.Swin[i] = address->Swin[i] & 0x0f;
                        ArtPorts.Swin[n + i] = address->Swin[i] & 0x0f;
                        if (address->Swin[i] & 0x80) {
                            ArtPollReply.PortType[i] |= 0x40;
                            ArtPorts.PortType[n + i] |= 0x40;
                        } else {
                            ArtPollReply.PortType[i] &= ~0x40;
                            ArtPorts.PortType[n + i] &= ~0x40;
                        }
                    }
                    if (address->Swout[i] != 0x7f) {
                        ArtPollReply.Swout[i] = address->Swout[i] & 0x0f;
                        ArtPorts.Swout[n + i] = address->Swout[i] & 0x0f;
                        if (address->Swout[i] & 0x80) {
                            ArtPollReply.PortType[i] |= 0x80;
                            ArtPorts.PortType[n + i] |= 0x80;
                        } else {
                            ArtPollReply.PortType[i] &= ~0x80;
                            ArtPorts.PortType[n + i] &= ~0x80;
                        }
                    }
                }

                if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
                SendArtPollReply(); // send a reply to the ArtPoll
                DBG(" <Art-Address> NetSwitch: %02x Command: %02x\r\n", NetSwitch, address->Command);
            }
            break;

        case OP_IpProg:  // 0xf800
            if (rxlen == sizeof(struct ArtIpProg_Packet)) {
                struct ArtIpProg_Packet *ipprog = (struct ArtIpProg_Packet *)buf;

                if (ipprog->Command & (1<<7)) {
                  if (ipprog->Command & (1<<6)) { // dhcp
                    BindIpAddress[0] = 0;
                  }
                  if (ipprog->Command & (1<<2)) { // ip addrress
                    sprintf(BindIpAddress, "%d.%d.%d.%d", ipprog->ProgIp[0], ipprog->ProgIp[1], ipprog->ProgIp[2], ipprog->ProgIp[3]);
//                    sprintf(DGateWay, "%d.%d.%d.%d", ipprog->Padding[0], ipprog->Padding[1], ipprog->Padding[2], ipprog->Padding[3]);
                  }
                  if (ipprog->Command & (1<<1)) { // subnet mask
                    sprintf(SubNetMask, "%d.%d.%d.%d", ipprog->ProgSm[0], ipprog->ProgSm[1], ipprog->ProgSm[2], ipprog->ProgSm[3]);
                  }
                  bcast(BCastAddress, BindIpAddress, SubNetMask);
                }

//                if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
                SendArtIpProgReply();
                DBG(" <Art-IpProg> NetSwitch: %02x Command: %02x\r\n", NetSwitch, ipprog->Command);
            }
                if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
            break;

        case OP_OpTodRequest:  // 0x8000
            if (rxlen == sizeof(struct ArtTodRequest_Packet)) {
                struct ArtTodRequest_Packet *todreq = (struct ArtTodRequest_Packet *)buf;
                if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
                for (i = 0; i < todreq->AddCount; i ++) {
                    SendArtTodData(todreq->Address[i]);
                }
            }
            break;

        case OP_OpTodControl:  // 0x8200
            if (rxlen == sizeof(struct ArtTodControl_Packet)) {
                struct ArtTodControl_Packet *tod = (struct ArtTodControl_Packet *)buf;
                if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
                if (tod->Command == 0x01) {
                    SendArtTodData(tod->Address);
                }
            }
            break;

        case OP_OpRdm:  // 0x8300
        case OP_OpRdmSub:  // 0x8400
        default:
            if (cb_ArtParser) cb_ArtParser(ArtHead, rxlen);
            DBG(" <Unknown> OpCode: %04x\r\n", ArtHead->OpCode);
            break;
       } // case packet type
   }

   rxlen = 0;
   return 0;
}

// socket shutdown
void DmxArtNet::Done () {
    _art.close();
}

// init ArtDMX packet
void DmxArtNet::Init_ArtDMX () {
   // header stuff
   memset(&ArtDMX, 0, sizeof(ArtDMX));
   memcpy(ArtDMX.ID, ArtHeaderID, 8);
   ArtDMX.OpCode = OP_Output;
   ArtDMX.Version = ArtVersion;
   // dmx stuff
   ArtDMX.Universes = 0; // this is the destination for the dmx
   ArtDMX.Sequence = 0;
   ArtDMX.Length = htons(DMX_SIZE);
}

// send ArtDmx Packet
int DmxArtNet::Send_ArtDmx(int univ, int physical, char *data, int length) {
   Endpoint Remote;

   // build a artdmx packet
   memcpy(ArtDMX.Data, data, length);
   ArtDMX.Universes = (NetSwitch & 0x7ff0) | univ;
   ArtDMX.Length = htons(length);
   ArtDMX.Physical = physical;
   ArtDMX.Sequence ++;

   // set place to send
   Remote.set_address(BCastAddress, ArtUDPPort);

   // send it off
   _art.sendTo(Remote, (char*)&ArtDMX, sizeof(ArtDMX));

   return 0;
}


void DmxArtNet::InitArtPollReplyDefaults () {
   memset(&ArtPollReply, 0, sizeof(ArtPollReply));
   memset(&ArtPorts, 0, sizeof(ArtPorts));
   memcpy(ArtPollReply.ID, ArtHeaderID, 8);
   ArtPollReply.OpCode = OP_PollReply; // reply packet
   ArtPollReply.Version = ArtVersion;

   ArtPollReply.NetSwitch = (NetSwitch >> 8) & 0x7f;
   ArtPollReply.SubSwitch = (NetSwitch >> 4) & 0x0f;
   ArtPollReply.OEM = htons(OemId);
   ArtPollReply.Status = (3<<6)|(2<<4); // Normal mode, Universe programmed by network

   ArtPollReply.Addr = localaddr;
   strncpy(ArtPollReply.ShortName, STR_ShortName, 18);
   strncpy(ArtPollReply.LongName, STR_LongName, 64);
   strncpy(ArtPollReply.NodeReport, "OK", 64);
   ArtPollReply.Style = StyleNode;

   mbed_mac_address((char*)&ArtPollReply.Mac);

   ArtPollReply.PortType[0] = 0;
   ArtPollReply.PortType[1] = 0;
   ArtPollReply.PortType[2] = 0;
   ArtPollReply.PortType[3] = 0;
}

// send ArtPoll Reply
int DmxArtNet::SendArtPollReply () {
    int i, j, n;

   // send the packet
//    DBG("SendArtPollReply> %s:%d\r\n", RemoteSin.get_address(), RemoteSin.get_port());
    if (ArtMaxUniv <= 4) {
        _art.sendTo(RemoteSin, (char*)&ArtPollReply, sizeof(ArtPollReply));
    } else {
        for (i = 0; i < (ArtMaxUniv + 3) / 4; i ++) {
            n = i * 4;
            ArtPollReply.BindIndex = 1 + i;
            ArtPollReply.NumPorts  = ArtMaxUniv - n < 4 ? ArtMaxUniv - n : 4;
            for (j = 0; j < ArtPollReply.NumPorts; j ++) {
                ArtPollReply.PortType[j]   = ArtPorts.PortType[n + j];
                ArtPollReply.GoodInput[j]  = ArtPorts.GoodInput[n + j];
                ArtPollReply.GoodOutput[j] = ArtPorts.GoodOutput[n + j];
                ArtPollReply.Swin[j]       = ArtPorts.Swin[n + j];
                ArtPollReply.Swout[j]      = ArtPorts.Swout[n + j];
            }
            for (; j < 4; j ++) {
                ArtPollReply.PortType[j]   = 0;
                ArtPollReply.GoodInput[j]  = 0;
                ArtPollReply.GoodOutput[j] = 0;
                ArtPollReply.Swin[j]       = 0;
                ArtPollReply.Swout[j]      = 0;
            }
            _art.sendTo(RemoteSin, (char*)&ArtPollReply, sizeof(ArtPollReply));
        }
    }
   return 0;
}

// send ArtIpProg Reply
int DmxArtNet::SendArtIpProgReply () {
    ArtIpProgReply_Packet ArtIpProgReply;
    int ip[4];

    memset(&ArtIpProgReply, 0, sizeof(ArtIpProgReply));
    memcpy(ArtIpProgReply.ID, ArtHeaderID, 8);
    ArtIpProgReply.OpCode = OP_IpProgReply;
    ArtIpProgReply.Version = ArtVersion;

    sscanf(BindIpAddress, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
    ArtIpProgReply.ProgIp[0] = ip[0];
    ArtIpProgReply.ProgIp[1] = ip[1];
    ArtIpProgReply.ProgIp[2] = ip[2];
    ArtIpProgReply.ProgIp[3] = ip[3];
    sscanf(SubNetMask, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
    ArtIpProgReply.ProgSm[0] = ip[0];
    ArtIpProgReply.ProgSm[1] = ip[1];
    ArtIpProgReply.ProgSm[2] = ip[2];
    ArtIpProgReply.ProgSm[3] = ip[3];
    ArtIpProgReply.ProgPortH = ArtUDPPort >> 8;
    ArtIpProgReply.ProgPort = ArtUDPPort & 0xff;

    if (BindIpAddress[0] == '0') {
        ArtIpProgReply.Status = 0x40; // dhcp
    }

   _art.sendTo(RemoteSin, (char*)&ArtIpProgReply, sizeof(ArtIpProgReply));

   return 0;
}

// send SendArtTod
int DmxArtNet::SendArtTodData (int n) {
    ArtTodData_Packet ArtTodData;

    memset(&ArtTodData, 0, sizeof(ArtTodData));
    memcpy(ArtTodData.ID, ArtHeaderID, 8);
    ArtTodData.OpCode = OP_OpTodData;
    ArtTodData.Version = ArtVersion;

    ArtTodData.RdmVersion = 1;
    ArtTodData.Port = n + 1;

    ArtTodData.UidTotalH = 0;
    ArtTodData.UidTotalL = 1;
    ArtTodData.BlockCount = 0;
    ArtTodData.UidCount = 2;
    ArtTodData.ToD[0] = 0x11;
    ArtTodData.ToD[1] = 0x22;

   _art.sendTo(RemoteSin, (char*)&ArtTodData, sizeof(ArtTodData));

   return 0;
}

void DmxArtNet::bcast(char *dest, char *ipaddr, char *netmask) {
    unsigned char ip[4];
    int a[4], n[4];

    sscanf(ipaddr, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
    sscanf(netmask, "%d.%d.%d.%d", &n[0], &n[1], &n[2], &n[3]);
    ip[0] = (a[0] & n[0]) | ~n[0];
    ip[1] = (a[1] & n[1]) | ~n[1];
    ip[2] = (a[2] & n[2]) | ~n[2];
    ip[3] = (a[3] & n[3]) | ~n[3];
    sprintf(dest, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}

void DmxArtNet::attach (void (*handler)(struct ArtPacketHeader *, int)) {
    cb_ArtParser = handler;
}