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; }