Hello
I’m trying to create a script that sends code written in Pixelblaze’s language through a WebSocket to the Pixelblaze, ensuring that the code is correctly processed and the pattern becomes fully functional.
When I attempted to implement this myself, I was able to create a new pattern that appeared on the device, but it seems to be improperly formatted and therefore not operational. Can you provide guidance or help me correct this?
import WebSocket from 'ws';
import CRC32 from 'crc-32';
// Replace with your Pixelblaze IP address
const pixelblazeUrl = 'ws://192.168.1.93:81'; // Change to your Pixelblaze IP
const ws = new WebSocket(pixelblazeUrl);
const chunkMaxSize = 8192; // Max size per chunk for ESP32
let outstandingPackets = 0;
let saveInProgress = null;
let sendQueue = [];
// Custom Pixelblaze pattern code (this is the real code to be sent and compiled)
const customPatternCode = `
export function render(index) {
hsv(index / pixelCount, 1, 1); // Simple rainbow pattern
}
`;
// Logging helper
function logStep(step) {
console.log(`\n--- ${step} ---`);
}
// On successful connection
ws.on('open', () => {
logStep('Connected to Pixelblaze');
// Start by saving a new custom pattern
saveNewPattern("AAA", customPatternCode);
});
// Prepare and compile the program, send the metadata, and then transmit the pattern data
function saveNewPattern(patternName, patternCode) {
const programBits = assembleProgramBits(patternName, patternCode);
const crc = CRC32.buf(new Uint8Array(programBits.binary)); // Calculate CRC for the pattern data
const message = {
setCode: {
size: programBits.binary.byteLength, // size of the binary in bytes
crc: crc >>> 0, // Ensure unsigned CRC value
name: programBits.name // Pattern name
}
};
logStep(`Saving new pattern: ${patternName}`);
// Send the metadata first
ws.send(JSON.stringify(message));
// Then send the actual program data in chunks
sendBlob(programBits.binary);
// Wait for save operation to complete, then request the program list
setTimeout(() => {
logStep('Requesting list of programs');
requestProgramList();
}, 3000); // Wait 3 seconds to ensure program is saved
}
// Assemble the program into a binary blob ready for transmission
function assembleProgramBits(patternName, patternCode) {
const codeBuffer = new TextEncoder().encode(patternCode);
// Create a binary representation of the program (could include additional metadata)
const binary = new Uint8Array(codeBuffer);
return {
name: patternName,
binary: binary // Return the compiled program as a Uint8Array (binary format)
};
}
// Send the binary data in chunks to handle large payloads
function sendBlob(binaryData) {
const totalSize = binaryData.byteLength;
let offset = 0;
function sendNextChunk() {
if (offset < totalSize) {
const chunkSize = Math.min(chunkMaxSize, totalSize - offset);
const chunk = binaryData.slice(offset, offset + chunkSize);
ws.send(chunk);
outstandingPackets++;
offset += chunkSize;
logStep(`Sent chunk: ${offset} / ${totalSize}`);
// Wait before sending the next chunk (optional delay to prevent flooding)
setTimeout(sendNextChunk, 50);
} else {
logStep('All chunks sent.');
}
}
sendNextChunk();
}
// Request the list of programs
function requestProgramList() {
const message = { listPrograms: true };
ws.send(JSON.stringify(message));
logStep('Sent request for program list');
}
// Set a specific pattern as active by its ID
function activateProgramById(programId) {
const message = { setProgram: programId };
ws.send(JSON.stringify(message));
logStep(`Activating program with ID: ${programId}`);
}
// Control brightness (0.0 to 1.0)
function setBrightness(level) {
const message = { brightness: level };
ws.send(JSON.stringify(message));
logStep(`Setting brightness to ${level}`);
}
// Set variables for a specific pattern
function setPatternVariables(variables) {
const message = { setVars: variables };
ws.send(JSON.stringify(message));
logStep('Setting pattern variables');
}
// On receiving a message from Pixelblaze
ws.on('message', (data) => {
let message = data.toString();
logStep('Received response from Pixelblaze');
console.log('Raw message:', message);
// Attempt to parse as JSON first
try {
const parsedMessage = JSON.parse(message);
logStep('Parsed JSON response');
console.log(parsedMessage);
// Handle specific JSON responses like system status
if (parsedMessage.programList) {
logStep('Program list received');
}
} catch (err) {
logStep('Failed to parse JSON response');
// Handle non-JSON message (likely the program list)
if (message.includes('\t')) {
logStep('Processing raw program list');
// Split the message by newlines to get each program
const programEntries = message.split('\n').filter(Boolean); // Removes empty lines
const programList = programEntries.map(entry => {
const [id, name] = entry.split('\t');
return { id, name };
});
console.log('Parsed program list:', programList);
// Example: Find and activate the newly saved program
const uploadedProgram = programList.find(program => program.name === "AAA");
if (uploadedProgram) {
logStep(`Uploaded program found: ${uploadedProgram.name} (ID: ${uploadedProgram.id})`);
activateProgramById(uploadedProgram.id);
// Set brightness after activating the pattern
setTimeout(() => setBrightness(0.8), 2000);
// Example: Set some pattern variables (customize based on the pattern)
setTimeout(() => setPatternVariables({ variableName: 1 }), 4000);
} else {
logStep('Uploaded program not found in program list');
}
} else {
console.log('Received non-JSON message:', message);
}
}
});
// On WebSocket close
ws.on('close', () => {
logStep('Disconnected from Pixelblaze');
});
// On WebSocket error
ws.on('error', (error) => {
logStep('Error encountered');
console.error('WebSocket error:', error);
});