/* Triple buffering data structure Example writing: ---------------- DMXTripleBuffer buffer; bytes_written = producer.write( buffer.fill_buffer + buffer.fill_pos, // destination pointer 512 - buffer.fill_pos // maximum allowed ); buffer.fill_pos += bytes_written; if buffer.fill_pos == 512 { buffer.on_fill_complete(); // swap buffers buffer.fill_pos = 0; // reset fill pos } */ struct InternalBuffer { char data[512] = {0}; bool is_fresh = false; }; class DMXTripleBuffer { public: DMXTripleBuffer() { fill_buffer = &_buffers[0]; drain_buffer = &_buffers[1]; off_buffer = &_buffers[2]; } void on_fill_complete() { fill_pos = 0; fill_buffer->is_fresh = true; std::swap(fill_buffer, off_buffer); } void on_drain_complete() { drain_pos = 0; drain_buffer->is_fresh = false; if (off_buffer->is_fresh) { std::swap(drain_buffer, off_buffer); } } InternalBuffer *fill_buffer, *drain_buffer, *off_buffer; size_t fill_pos = 0, drain_pos = 0; private: InternalBuffer _buffers[3]; }; /* Some globals */ DMXTripleBuffer buffer; // Triple buffer instance bool packet_ready = true; // flag to write header // send a "Sync." every second if no data is coming in unsigned long time_since_last_sync; const unsigned long SYNC_TIMEOUT = 1000; /* setup initialize both Serial connections and write the initial "Sync." */ void setup() { Serial.begin(500000); // USB Serial.setRxBufferSize(512); // to fit the full DMX packet. (default is 128) while (!Serial) { // spin until serial is up } Serial1.begin(250000, SERIAL_8N2); // DMX while (!Serial1) { // spin until serial1 is up } Serial.println(); Serial.println("Sync."); time_since_last_sync = millis(); } /* loop continuously poll Serial1 for writing and Serial for reading only read/write as much as fits into the UART buffers to avoid blocking since Serial1 is set to 250000 baud, it should be able to write 50 DMX packets per second */ void loop() { // output if (packet_ready) { send_dmx_header(); packet_ready = false; } size_t n = Serial1.availableForWrite(); size_t written = Serial1.write( buffer.drain_buffer->data + buffer.drain_pos, std::min(n, 512 - buffer.drain_pos)); buffer.drain_pos += written; if (buffer.drain_pos == 512) { buffer.on_drain_complete(); packet_ready = true; } // input n = Serial.available(); unsigned long now = millis(); if (!n) { if (now - time_since_last_sync > 1000) { buffer.fill_pos = 0; Serial.println("Sync."); time_since_last_sync = now; } return; } time_since_last_sync = now; size_t read = Serial.read( buffer.fill_buffer->data + buffer.fill_pos, std::min(n, 512 - buffer.fill_pos)); buffer.fill_pos += read; if (buffer.fill_pos == 512) { buffer.on_fill_complete(); Serial.println("Ack."); } } void send_dmx_header() { Serial1.flush(); Serial1.begin(90000, SERIAL_8N2); // send the break as a "slow" byte Serial1.write(0); // switch back to the original baud rate Serial1.flush(); Serial1.begin(250000, SERIAL_8N2); Serial1.write(0); // Start-Byte }