Skip to content

Commit

Permalink
Add esp32 chunked udp device and improve FPS timing in program genera…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
jsilveira committed Jan 26, 2022
1 parent 55d1aaf commit 7eba1a8
Show file tree
Hide file tree
Showing 13 changed files with 596 additions and 86 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

node_modules

audio/venv

npm-debug.log
arduino/**/*.hex
arduino/**/*.elf
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,13 @@ En `PROGRAM_Transition.js` pueden ver un ejemplo de agarrar varios programas dis
Si estás familiarizado con la IDE de Arduino podés usarla para compilar y subir los scripts del directorio `arduino/`. Recordá configurar la localización del proyecto (sketchbook) al directorio `arduino/` de este repositorio.

Si preferís usar tu editor favorito, la herramienta `arduino-cli` también funciona muy bien. Podés encontrar instrucciones completas de cómo bajarla y usarla en https://github.com/arduino/arduino-cli.

### esp32 Olimex

Leer https://www.olimex.com/Products/IoT/ESP32/_resources/Arudino-ESP32.txt para compilar programas en arduino Ide

Ejemplos básicos de programa que usa ethernet:
- https://raw.githubusercontent.com/espressif/arduino-esp32/1.0.3/libraries/WiFi/examples/ETH_LAN8720/ETH_LAN8720.ino
- https://github.com/OLIMEX/ESP32-POE/blob/master/SOFTWARE/ARDUINO/ESP32_PoE_Ethernet_Arduino/ESP32_PoE_Ethernet_Arduino.ino

Nota: El MTU default de esp32 y UDP es 1500 bytes. Al mandar paquetes más grandes nada falla pero nunca llegan.
264 changes: 264 additions & 0 deletions arduino/esp32-ethernet/esp32-ethernet.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// This is needed to prevent horrible crashes of esp32 probably caused by ethernet interrupts when data arrives
#define FASTLED_ALLOW_INTERRUPTS 0
//#define FASTLED_INTERRUPT_RETRY_COUNT 1

#include <ETH.h>
#include <WiFiUdp.h>
#include <FastLED.h>


// Ethernet and connection protocol stuff
static bool eth_connected = false;
bool connected = false;

unsigned long lastPerfStatus = millis();
unsigned long lastFrame = millis();

int frameCount = 0;
int count = 0;
byte lastC = 0;

WiFiUDP udp; // create UDP object


// Protocol defined strings coupled with lights server
char StringPerf[] = "PERF";
char StringAlive[] = "YEAH";


// ============================================================================
// COMPILE TIME CONFIG GOES HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#define STRIP_NUM_LEDS 300 // EACH ONE of the two strips will have that many leds

#define DATA_PIN 2
#define DATA_PIN2 16

#define POWER_MILLIAMPS 1000 // Max combined mah power consumption by all the strips

unsigned int localUdpPort = 2222; // Local port number

// ============================================================================


// LED strips data structures and logic

constexpr byte ENCODING_RGB = 4; // The only supported encoding by this device

int NUM_LEDS = STRIP_NUM_LEDS*2; // Total number of leds in all strips
char ledsBuffer[2 * 3 * 150 + 2]; // buffer to hold incoming packet

// Define the array of leds
CRGB leds[STRIP_NUM_LEDS * 2];

void setup() {
Serial.begin(250000);
Serial.println("Serial connected");

// setupUDPConnection(2222, 2); // ESP32 ETH 2
// setupUDPConnection(4444, 4); // ESP32 ETH 4

WiFi.onEvent(WiFiEvent);
ETH.begin();

// Static ethernet config. Comment out for automatic DHCP
ETH.config(
IPAddress(192, 168, 2, 101),
IPAddress(192, 168, 2, 1),
IPAddress(255, 255, 255, 0),
IPAddress(192, 168, 2, 1),
IPAddress(192, 168, 2, 1)
);

setupLeds(600, 2, 16);
}


void broadcastAlive() {
Serial.println("Broadcasting I exist...");

IPAddress remoteIp(255, 255, 255, 255);

// Broadcast a metadata string with the form "YEAH <field1>=<value1> <field2>=<value2>..."
udp.beginPacket(remoteIp, localUdpPort);
udp.print(StringAlive);
udp.print(" leds=");
udp.print(STRIP_NUM_LEDS*2);
udp.print(" datapin1=");
udp.print(DATA_PIN);
udp.print(" datapin2=");
udp.print(DATA_PIN2);
udp.endPacket();
}

void broadcastPerf(int frames) {
IPAddress remoteIp(255, 255, 255, 255);
udp.beginPacket(remoteIp, localUdpPort);
udp.print(StringPerf);
String framesString = String(frames);
char frameChar[5];
framesString.toCharArray(frameChar, 5);
udp.print(frameChar);
udp.endPacket();

Serial.print("Broadcasting PERF ");
Serial.println(frameChar);
}

bool checkForNewUDPMsg(char packetBuffer[]) {
int packetSize = udp.parsePacket();

if (packetSize == 0) {
return false;
}

// // For debugging
//Serial.print("Received packet of size ");
//Serial.println(packetSize);

udp.read(packetBuffer, packetSize);

byte c = packetBuffer[0];
if (c - lastC > 1) {
Serial.print("Missed ");
Serial.print(c - lastC - 1, DEC);
Serial.print(" - packet #");
Serial.println(c, DEC);
}

// // For debugging
// if ((c % 50) == 0) {
// Serial.print("Received packet #");
// Serial.println(c, DEC);
// }

lastC = c;

return true;
}

void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case SYSTEM_EVENT_ETH_START:
Serial.println("ETH Started");
//set eth hostname here
ETH.setHostname("esp32-ethernet");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
Serial.println("ETH Connected");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
Serial.print("ETH MAC: ");
Serial.print(ETH.macAddress());
Serial.print(", IPv4: ");
Serial.print(ETH.localIP());
if (ETH.fullDuplex()) {
Serial.print(", FULL_DUPLEX");
}
Serial.print(", ");
Serial.print(ETH.linkSpeed());
Serial.println("Mbps");

// Start UDP port and set global eth_connected flag
udp.begin(localUdpPort);
Serial.print("Begin udp in port ");
Serial.print(localUdpPort);
Serial.println(".");

eth_connected = true;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
break;
case SYSTEM_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
}
}

void setupLeds(int numLeds, int dataPin1, int dataPin2)
{
NUM_LEDS = numLeds;

FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, 0, STRIP_NUM_LEDS);
FastLED.addLeds<WS2812B, DATA_PIN2, GRB>(leds, STRIP_NUM_LEDS, STRIP_NUM_LEDS);

FastLED.setMaxPowerInVoltsAndMilliamps(5, POWER_MILLIAMPS);

FastLED.showColor(CRGB::Black);

for (int i = 0; i < 2; i++)
{
leds[0 + i * STRIP_NUM_LEDS] = CRGB::Black;
leds[1 + i * STRIP_NUM_LEDS] = CRGB::Red;
leds[2 + i * STRIP_NUM_LEDS] = CRGB::Green;
leds[3 + i * STRIP_NUM_LEDS] = CRGB::Blue;
}

FastLED.show();
}

void writeLedFrame(char data[], int offset)
{
int chunk = data[0 + offset];
int encoding = data[1 + offset];

int chunkOffset = chunk*300;
int ledsInPacket = NUM_LEDS;
if(ledsInPacket > 300) {
ledsInPacket = min(NUM_LEDS, chunkOffset + 300) - chunkOffset;
}

if (encoding == ENCODING_RGB)
{
for (int i = 0; i < ledsInPacket; i++)
{
int r = data[2 + i * 3 + offset];
int g = data[2 + 1 + i * 3 + offset];
int b = data[2 + 2 + i * 3 + offset];

leds[i+chunkOffset].setRGB(r, g, b);
}
}
else
{
Serial.println("Unexpected encoding byte");
}

if(chunk == ceil(NUM_LEDS/300) - 1) {
FastLED.show();
frameCount++;
lastFrame = millis();;
}
}

void loop() {
if (!eth_connected) {
return;
}

unsigned long nowMs = millis();
if (nowMs - lastPerfStatus > 1000) {
lastPerfStatus = nowMs;
if (!connected) {
broadcastAlive();
} else {
broadcastPerf(frameCount);
frameCount = 0;
}
}

if (checkForNewUDPMsg(ledsBuffer)) {
writeLedFrame(ledsBuffer, 1);
connected = true;
} else {
if (nowMs - lastFrame > 2000) {
connected = false;
}
}
}
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dgram": "^1.0.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"nanotimer": "^0.3.15",
"node-fetch": "^2.6.6",
"performance-now": "^2.1.0",
"pino": "^5.15.0",
Expand Down
36 changes: 5 additions & 31 deletions server/setups/conga.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,13 @@
"shapeMapping": "conga",
"lights": 645,
"outputDevices": {
"serial1": {
"type": "serial",
"params": {
"numberOfLights": 45,
"baudRate": 1500000,
"devicePortWindows": "COM3",
"devicePortUnix": "/dev/ttyACM0"
}
},
"serial2": {
"type": "serial",
"params": {
"numberOfLights": 300,
"baudRate": 1500000,
"devicePortWindows": "COM4",
"devicePortUnix": "/dev/ttyACM0"
}
},
"serialTest": {
"type": "serial",
"params": {
"numberOfLights": 600,
"baudRate": 1500000,
"devicePortWindows": "COM5",
"devicePortUnix": "/dev/ttyACM0"
}
},
"udp": {
"type": "udp-wled",
"type": "udp-chunked",
"params": {
"numberOfLights": 300,
"ip": "192.168.0.177",
"udpPort": 21324
"name": "",
"ip": "192.168.2.101",
"udpPort": 2222
}
}
},
Expand All @@ -44,7 +18,7 @@
"from": 0,
"to":600,
"baseIndex": 0,
"deviceName": "serialTest"
"deviceName": "udp"
}
]
}
7 changes: 6 additions & 1 deletion server/src/DeviceMultiplexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const _ = require('lodash');
const DeviceSerial = require('./devices/serial');
const DeviceUDP = require('./devices/udp');
const DeviceUDPWLED = require('./devices/udp-wled');
const LightDeviceUDPChunked = require("./devices/udp-chunked");

function initDevicesFromConfig(outputDevices) {
let devices = {};
Expand All @@ -19,6 +20,9 @@ function initDevicesFromConfig(outputDevices) {
case 'udp-wled':
device = new DeviceUDPWLED(params);
break;
case 'udp-chunked':
device = new LightDeviceUDPChunked(params);
break;
default:
throw new Error(`Invalid device type: ${type}`);
}
Expand Down Expand Up @@ -91,7 +95,8 @@ module.exports = class DeviceMultiplexer {
return {
status: d.status,
deviceId: d.deviceId,
lastFps: d.lastFps
lastFps: d.lastFps,
metadata: d.metadata || {}
};
})
);
Expand Down
Loading

0 comments on commit 7eba1a8

Please sign in to comment.