forked from forkineye/ESPixelStick
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ESPAsyncZCPP.cpp
211 lines (187 loc) · 7.56 KB
/
ESPAsyncZCPP.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
* ESPAsyncZCPP.cpp
*
* Project: ESPAsyncZCPP - Asynchronous ZCPP library for Arduino ESP8266 and ESP32
* Copyright (c) 2019 Keith Westley
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
#include "ESPAsyncZCPP.h"
#include <string.h>
// Constructor
ESPAsyncZCPP::ESPAsyncZCPP(uint8_t buffers) {
pbuff = RingBuf_new(sizeof(ZCPP_packet_t), buffers);
suspend = false;
stats.num_packets = 0;
stats.packet_errors = 0;
}
/////////////////////////////////////////////////////////
//
// Public begin() members
//
/////////////////////////////////////////////////////////
bool ESPAsyncZCPP::begin(IPAddress ourIP) {
suspend = false;
return initUDP(ourIP);
}
/////////////////////////////////////////////////////////
//
// Private init() members
//
/////////////////////////////////////////////////////////
bool ESPAsyncZCPP::initUDP(IPAddress ourIP) {
bool success = false;
delay(100);
IPAddress address = IPAddress(224, 0, 30, 5);
if (udp.listenMulticast(address, ZCPP_PORT)) {
udp.onPacket(std::bind(&ESPAsyncZCPP::parsePacket, this,
std::placeholders::_1));
success = true;
}
ip_addr_t ifaddr;
ip_addr_t multicast_addr;
ifaddr.addr = static_cast<uint32_t>(ourIP);
multicast_addr.addr = static_cast<uint32_t>(IPAddress(224, 0, 31, (ifaddr.addr & 0xFF000000) >> 24));
igmp_joingroup(&ifaddr, &multicast_addr);
if (Serial) {
Serial.print("ZCPP subscribed to multicast 224.0.31.");
Serial.println((ifaddr.addr & 0xFF000000) >> 24);
}
return success;
}
/////////////////////////////////////////////////////////
//
// Packet parsing - Private
//
/////////////////////////////////////////////////////////
void ESPAsyncZCPP::parsePacket(AsyncUDPPacket _packet) {
ZCPP_error_t error = ERROR_ZCPP_NONE;
sbuff = reinterpret_cast<ZCPP_packet_t *>(_packet.data());
if (memcmp(sbuff->Discovery.Header.token, ZCPP_token, sizeof(sbuff->Discovery.Header.token))) {
error = ERROR_ZCPP_ID;
}
if (!error && sbuff->Discovery.Header.type != ZCPP_TYPE_DISCOVERY &&
sbuff->Discovery.Header.type != ZCPP_TYPE_CONFIG &&
sbuff->Discovery.Header.type != ZCPP_TYPE_QUERY_CONFIG &&
sbuff->Discovery.Header.type != ZCPP_TYPE_SYNC &&
sbuff->Discovery.Header.type != ZCPP_TYPE_DATA) {
error = ERROR_ZCPP_IGNORE;
}
if (!error && sbuff->Discovery.Header.protocolVersion > ZCPP_CURRENT_PROTOCOL_VERSION) {
if (sbuff->Discovery.Header.type == 0x00) {
error = ERROR_ZCPP_NONE;
}
else if (!error) {
error = ERROR_ZCPP_PROTOCOL_VERSION;
}
}
if (!error && (!suspend || sbuff->Discovery.Header.type == ZCPP_TYPE_DISCOVERY)) {
if (sbuff->Discovery.Header.type == ZCPP_TYPE_DISCOVERY ||
sbuff->Discovery.Header.type == ZCPP_TYPE_QUERY_CONFIG ||
(sbuff->Discovery.Header.type == ZCPP_TYPE_CONFIG && (sbuff->Configuration.flags & ZCPP_CONFIG_FLAG_QUERY_CONFIGURATION_RESPONSE_REQUIRED) != 0)) {
suspend = true;
}
pbuff->add(pbuff, sbuff);
stats.num_packets++;
stats.last_clientIP = _packet.remoteIP();
stats.last_clientPort = _packet.remotePort();
stats.last_seen = millis();
} else if (error == ERROR_ZCPP_IGNORE || suspend) {
// Do nothing
} else {
if (Serial)
dumpError(error);
stats.packet_errors++;
}
}
/////////////////////////////////////////////////////////
//
// Debugging functions - Public
//
/////////////////////////////////////////////////////////
void ESPAsyncZCPP::dumpError(ZCPP_error_t error) {
switch (error) {
case ERROR_ZCPP_ID:
Serial.print(F("INVALID PACKET ID: "));
for (uint i = 0; i < sizeof(ZCPP_token); i++)
Serial.print(sbuff->Discovery.Header.token[i], HEX);
Serial.println("");
break;
case ERROR_ZCPP_PROTOCOL_VERSION:
Serial.print(F("INVALID PROTOCOL VERSION: 0x"));
Serial.println(sbuff->Discovery.Header.protocolVersion, HEX);
break;
case ERROR_ZCPP_NONE:
break;
case ERROR_ZCPP_IGNORE:
break;
}
}
void ESPAsyncZCPP::sendConfigResponse(ZCPP_packet_t* packet)
{
if (udp.writeTo(packet->raw, sizeof(ZCPP_packet_t), stats.last_clientIP, stats.last_clientPort) != sizeof(ZCPP_packet_t)) {
if (Serial)
Serial.println("Write of configuration response failed");
}
else {
if (Serial) {
Serial.print("Configuration response wrote ");
Serial.print(sizeof(ZCPP_packet_t));
Serial.println(" bytes.");
}
}
suspend = false;
}
void ESPAsyncZCPP::sendDiscoveryResponse(ZCPP_packet_t* packet, const char* firmwareVersion, const uint8_t* mac, const char* controllerName, int pixelPorts, int serialPorts, uint32_t maxPixelPortChannels, uint32_t maxSerialPortChannels, uint32_t maximumChannels, uint32_t ipAddress, uint32_t ipMask)
{
memset(packet, 0x00, sizeof(ZCPP_packet_t));
memcpy(packet->DiscoveryResponse.Header.token, ZCPP_token, sizeof(ZCPP_token));
packet->DiscoveryResponse.Header.type = ZCPP_TYPE_DISCOVERY_RESPONSE;
packet->DiscoveryResponse.Header.protocolVersion = ZCPP_CURRENT_PROTOCOL_VERSION;
packet->DiscoveryResponse.minProtocolVersion = ZCPP_CURRENT_PROTOCOL_VERSION;
packet->DiscoveryResponse.maxProtocolVersion = ZCPP_CURRENT_PROTOCOL_VERSION;
packet->DiscoveryResponse.vendor = ntohs(ZCPP_VENDOR_ESPIXELSTICK);
packet->DiscoveryResponse.model = ntohs(0);
strncpy(packet->DiscoveryResponse.firmwareVersion, firmwareVersion, std::min((int)strlen(firmwareVersion), (int)sizeof(packet->DiscoveryResponse.firmwareVersion)));
memcpy(packet->DiscoveryResponse.macAddress, mac, sizeof(packet->DiscoveryResponse.macAddress));
packet->DiscoveryResponse.ipv4Address = ipAddress;
packet->DiscoveryResponse.ipv4Mask = ipMask;
strncpy(packet->DiscoveryResponse.userControllerName, controllerName, std::min((int)strlen(controllerName), (int)sizeof(packet->DiscoveryResponse.userControllerName)));
packet->DiscoveryResponse.pixelPorts = pixelPorts;
packet->DiscoveryResponse.rsPorts = serialPorts;
packet->DiscoveryResponse.channelsPerPixelPort = ntohs(maxPixelPortChannels);
packet->DiscoveryResponse.channelsPerRSPort = ntohs(maxSerialPortChannels);
packet->DiscoveryResponse.maxTotalChannels = ntohl(maximumChannels);
uint32_t protocolsSupported = 0;
if (pixelPorts > 0) {
protocolsSupported |= ZCPP_DISCOVERY_PROTOCOL_WS2811;
protocolsSupported |= ZCPP_DISCOVERY_PROTOCOL_GECE;
}
if (serialPorts > 0) {
protocolsSupported |= ZCPP_DISCOVERY_PROTOCOL_DMX;
protocolsSupported |= ZCPP_DISCOVERY_PROTOCOL_RENARD;
}
packet->DiscoveryResponse.protocolsSupported = ntohl(protocolsSupported);
packet->DiscoveryResponse.flags = ZCPP_DISCOVERY_FLAG_SEND_DATA_AS_MULTICAST;
if (udp.writeTo(packet->raw, sizeof(packet->DiscoveryResponse), stats.last_clientIP, stats.last_clientPort) != sizeof(packet->DiscoveryResponse)) {
if (Serial)
Serial.println("Write of discovery response failed");
}
else {
if (Serial) {
Serial.print("Discovery response wrote ");
Serial.print(sizeof(packet->DiscoveryResponse));
Serial.println(" bytes.");
}
}
suspend = false;
}