Good afternoon, I have a DMX light control project through the HW-519 module and an arduino Mega. It works perfectly for me but I need more speed. I've thought about using the Arduino Giga R1 but I can't find a way to convert the program. In Mega I use the Concentronics.h library which is phenomenal but is not compatible with the Giga. Has anyone developed a program with basic programming for DMX control from the Giga? Does anyone know of a DMX library compatible with Giga?
Thanks in advance.
Well me not and i don't know.
There was an earlier (and rather short) topic.
Do you really need that many pins and what is your end goal, because maybe an ESP of some kind would be better and easier to achieve that. The trick with DMX is that if you send, you send 513 bytes, and then you wait for at least 88us to reset the channel counter. Some of the UART FIFO's are so complex that to program such a thing is really tricky. The UART on the AVR (UNO or Mega) are fairly easy to program for such a task.
So the question really is : are you sending or receiving ? If you are receiving and you do need all those pins, it might be easier to split it into smaller processors each receiving DMX.
Anyway, it can be done on an ESP(8266 or 32) and i think it can be done on a teensy already.
Could you provide a link?
Thank you for the prompt response. The problem I have is that the Mega controls the HW-519 but also an HC06 module for Bluetooth connection and a DFPlayerMini for music playback. Everything works very well for me, controlling the light gradient up to 50 channels, the processing time is short and generates delays that distort the whole process. I had thought to solve the problem with a faster processor like the Giga or the Due but I can't find any library that works and I haven't managed to program a basic DMX control with the Serial output to the HW-519 either.
That's why I'm looking for an example of a basic DMX control program through an HW-519 module (or any based on the MAX485 or MAX3485) from an Arduino Giga.
Sorry, I meant "Conceptinetics.h."
Here's the link:
arduino-libraries/Conceptinetics/Conceptinetics.h at master · alfo/arduino-libraries · GitHub.
It's recommended when using the CQRobot DMX Shield MAX485.
I would say that an ESP32 would be up to the task with builtin BT and 3 hwUARTs, even when using DMX for one of those and assuming that the DFPlayer is also communicating over Serial, it looks like you still have one to spare, and could even use the HC-06 instead to simplify the migration.
Not quite sure what it is you are saying here. I would have to see your code to make this a bit more clear. Do you need that many pins ? could you use multiplexers instead, why is there so much processing time involved.
I know, i ran into issues with more complex processors as well. It's creating the break that is the tricky bit.
So far i don't know of one.
A more straightforward solution (if maybe a temporary one) is using an extra MCU do the DMX work. This will induce some latency, but it will be minimal and constant. You could use an extra Atmega 328p board and control that through SPI (i considered that before but the SPI communication would be a new learning and in the end the ESP32 managed without to much issue) or as i would choose in your situation, use an ESP-12F (ESP8266) and use the Serial.swap() pins for communication over Serial with the main board, and use GPIO 3 for I2S out using Makuna NeoPixelBus the DMX512 method. That would be the easiest for me to get working, and i know it will.
Great library for an AVR. I use it as well for transmission (built a small handheld DMX-controller with an UNO and an IL9341 to test DMX setups and receivers.
There is DmxSimple but have no idea if it supports any of the boards you want to use. Does seem to support ESP32 as well though, so it's worth investigating even just for that. (note to my self)
That actually failed to compile for an ESP32, but as i was looking i found SparkFunDMX.h which does compile on an ESP32 though i have yet to test the functionality. Unfortunately it doesn't compile for the GiGa, probably due to a different way in which hardwareSerial.h has been implemented in the core, but the SRC may provide you with a way of at least implementing your own DMX out on the Giga.
n.b for completion's sake the errors of the DMXOutput.ino compile for the Giga
Example1-DMXOutput:28:27: error: no matching function for call to 'arduino::HardwareSerial::HardwareSerial(int)'
HardwareSerial dmxSerial(2);
^
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Users\deva_\AppData\Local\Temp\arduino_build_553507\sketch\Example1-DMXOutput.ino.cpp:1:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: candidate: arduino::HardwareSerial::HardwareSerial()
class HardwareSerial : public Stream
^~~~~~~~~~~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: candidate expects 0 arguments, 1 provided
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: candidate: constexpr arduino::HardwareSerial::HardwareSerial(const arduino::HardwareSerial&)
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: no known conversion for argument 1 from 'int' to 'const arduino::HardwareSerial&'
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: candidate: constexpr arduino::HardwareSerial::HardwareSerial(arduino::HardwareSerial&&)
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: no known conversion for argument 1 from 'int' to 'arduino::HardwareSerial&&'
Example1-DMXOutput:28:16: error: cannot declare variable 'dmxSerial' to be of abstract type 'arduino::HardwareSerial'
HardwareSerial dmxSerial(2);
^~~~~~~~~
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Users\deva_\AppData\Local\Temp\arduino_build_553507\sketch\Example1-DMXOutput.ino.cpp:1:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:88:7: note: because the following virtual functions are pure within 'arduino::HardwareSerial':
class HardwareSerial : public Stream
^~~~~~~~~~~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:98:20: note: virtual size_t arduino::HardwareSerial::write(uint8_t)
virtual size_t write(uint8_t) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:97:18: note: virtual void arduino::HardwareSerial::flush()
virtual void flush(void) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:94:17: note: virtual int arduino::HardwareSerial::available()
virtual int available(void) = 0;
^~~~~~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:96:17: note: virtual int arduino::HardwareSerial::read()
virtual int read(void) = 0;
^~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:95:17: note: virtual int arduino::HardwareSerial::peek()
virtual int peek(void) = 0;
^~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:91:18: note: virtual void arduino::HardwareSerial::begin(long unsigned int)
virtual void begin(unsigned long) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:92:18: note: virtual void arduino::HardwareSerial::begin(long unsigned int, uint16_t)
virtual void begin(unsigned long baudrate, uint16_t config) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:93:18: note: virtual void arduino::HardwareSerial::end()
virtual void end() = 0;
^~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:100:13: note: virtual arduino::HardwareSerial::operator bool()
virtual operator bool() = 0;
^~~~~~~~
Using library SparkFun_DMX_Shield_Library at version 2.0.0 in folder: C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library
exit status 1
no matching function for call to 'arduino::HardwareSerial::HardwareSerial(int)'
Seems to be caused by the way the hwSerial object is named initially, there might be a rather simple solution for it, but for that a look into hardwareSerial.h for the Giga will most likely be required.
This is the point in the sketch
// Create serial port to be used for DMX interface. Exact implementation depends
// on platform, this example is for the ESP32
HardwareSerial dmxSerial(2);
Actually that was easily fixed.
//HardwareSerial dmxSerial(2);
#define dmxSerial Serial2
but some other errors remained
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp: In static member function 'static void SparkFunDMX::setComDir(bool)':
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:57:28: error: no matching function for call to 'arduino::HardwareSerial::flush(bool)'
_dmxSerial->flush(false);
^
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.h:19,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:17:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:97:18: note: candidate: virtual void arduino::HardwareSerial::flush()
virtual void flush(void) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:97:18: note: candidate expects 0 arguments, 1 provided
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp: In static member function 'static bool SparkFunDMX::update()':
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:119:21: error: 'class arduino::HardwareSerial' has no member named 'updateBaudRate'
_dmxSerial->updateBaudRate(breakBaud);
^~~~~~~~~~~~~~
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:122:28: error: call of overloaded 'write(int)' is ambiguous
_dmxSerial->write(0);
^
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.h:19,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:17:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:98:20: note: candidate: virtual size_t arduino::HardwareSerial::write(uint8_t)
virtual size_t write(uint8_t) = 0;
^~~~~
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/ArduinoAPI.h:31:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:27,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.h:19,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:17:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/Print.h:51:12: note: candidate: size_t arduino::Print::write(const char*)
size_t write(const char *str) {
^~~~~
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:126:21: error: 'class arduino::HardwareSerial' has no member named 'updateBaudRate'
_dmxSerial->updateBaudRate(DMX_BAUD);
^~~~~~~~~~~~~~
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:152:36: error: no matching function for call to 'arduino::HardwareSerial::flush(bool)'
_dmxSerial->flush(false);
^
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.h:19,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:17:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:97:18: note: candidate: virtual void arduino::HardwareSerial::flush()
virtual void flush(void) = 0;
^~~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:97:18: note: candidate expects 0 arguments, 1 provided
C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:157:50: error: no matching function for call to 'arduino::HardwareSerial::read(uint8_t [513], uint16_t&)'
_dmxSerial->read(_dmxBuffer, _numChannels);
^
In file included from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Serial.h:25:0,
from C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/Arduino.h:104,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.h:19,
from C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library\src\SparkFunDMX.cpp:17:
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:96:17: note: candidate: virtual int arduino::HardwareSerial::read()
virtual int read(void) = 0;
^~~~
C:\Users\deva_\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.3.1\cores\arduino/api/HardwareSerial.h:96:17: note: candidate expects 0 arguments, 2 provided
Using library SparkFun_DMX_Shield_Library at version 2.0.0 in folder: C:\Data\Arduino\Sketches\libraries\SparkFun_DMX_Shield_Library
exit status 1
Error compiling for board Arduino Giga R1.
The error with flush() should be investigated, what does that 'false' actually mean in most cores and why is it absent in the Giga core ?
The reading of a whole block from the serial buffer isn't actually relevant, since you aim to transmit,
The _dmxSerial->updateBaudRate(DMX_BAUD); error is actually the most troublesome, since that function does not seem to exist, another way of updating the baudrate must be found, that said, it may be possible to just 'add' it to the core, but it is the most tricky bit. How to modify the Giga UART baudrate without re-initializing the UART completely.
Ok, did some testing on the ESP32, and the Sparkfun does work, though there are some small issues. First of the update rate should be limited, to a framerate that is lower than what can be sent. In other words, it's goot be finished with the process before it is re-initiated. For 512 channels not higher than 40Hz (25ms is near what it takes to sent a frame.)
Also i upped the breaklength to 136us, which is the same as what is used by Conceptinetics.
Then it works beautifully on ESP32.
So i looked up what the 'false' is for in
_dmxSerial->flush(false);
and that is for an overloaded function call that can limit it to just tx, so that can just simply be removed and then that part should also compile on a Giga.
Can probably be replaced by a call to begin() but Conceptinetics uses a completely different method, which just switches the pinMode to OUTPUT and controls the pin as a normal GPIO (this will for sure not work on an ESP32) and this may also work on a Giga. Thing is Conceptinetics controls the TX interrupt directly through the registers, and these registers may exist on an Giga as well, but they would have a different name.
I don't own a Giga, so i can ot test anything other than what compiles.
Ah fixed that by adding another
_dmxSerial->flush();
before the change to the baudrate.
So the write function now looks like
bool SparkFunDMX::update()
{
if(_comDir == DMX_WRITE_DIR)
{
// We need to send a break signal to indicate the start of the message.
// Arduino doesn't really have a way to actually send a break signal, so
// this is a hacky solution: the baud rate is reduced such that sending
// a zero byte creates a sufficiently long pulse to simulate a break
// Reduce baud rate
uint32_t breakBaud = 1000000 * 8 / DMX_BREAK_DURATION_MICROS;
_dmxSerial->flush(); // how about we add this here ??
_dmxSerial->updateBaudRate(breakBaud);
// Send a zero at this new baud rate
_dmxSerial->write(0);
_dmxSerial->flush();
// Return baud rate to original value
_dmxSerial->updateBaudRate(DMX_BAUD);
// Send message
_dmxSerial->write(_dmxBuffer, _numChannels);
// Success
return true;
}
// else _comDir == DMX_READ_DIR
// Check if we've received the amount of data expected, +1 for break signal
else if(_dmxSerial->available() >= (_numChannels + 1))
{
// We need to detect a break signal indicating the start of the message.
// Arduino doesn't really have a way to actually read a break signal, it
// will instead appear as an extra zero byte at the start. This has the
// risk of not synchronizing properly, but it should be resolved by
// calling update() freuently enough (ie. faster than messages are sent)
// and flushing the RX buffer if not synced. We can do one other check:
// after the break signal, channel 0 is sent, which should always have a
// value of zero
// Read out break signal (hopefully!) and peek at channel 0 (hopefully!)
if((_dmxSerial->read() != 0) || (_dmxSerial->peek() != 0))
{
// If we get here, then we're not synced properly. We can try to
// flush out all the data and hope the next time is synced
_dmxSerial->flush(false);
return false;
}
// We're probably synced! Read data into buffer
_dmxSerial->read(_dmxBuffer, _numChannels);
// Set flag indicating we have new data
_dataAvailable = true;
// Success
return true;
}
// Something went wrong (most likely haven't received all the bytes yet)
return false;
}
For a GiGa the 'false' needs to eb removed, but since you only want to transmit you could leav the whole reception thing out altogether.
The write uodate for the GiGa should probably look like this
if(_comDir == DMX_WRITE_DIR)
{
// We need to send a break signal to indicate the start of the message.
// Arduino doesn't really have a way to actually send a break signal, so
// this is a hacky solution: the baud rate is reduced such that sending
// a zero byte creates a sufficiently long pulse to simulate a break
// Reduce baud rate
uint32_t breakBaud = 1000000 * 8 / DMX_BREAK_DURATION_MICROS;
_dmxSerial->flush(); // how about we add this here ??
//_dmxSerial->updateBaudRate(breakBaud);
_dmxSerial->begin(breakBaud, DMX_FORMAT);
// Send a zero at this new baud rate
_dmxSerial->write(0);
_dmxSerial->flush();
// Return baud rate to original value
//_dmxSerial->updateBaudRate(DMX_BAUD);
_dmxSerial->begin(DMX_BAUD, DMX_FORMAT);
// Send message
_dmxSerial->write(_dmxBuffer, _numChannels);
// Success
return true;
}
// els
Sorry, I've been on vacation for a few days and haven't been able to follow your tests. First of all, thank you for your dedication to the problem. I don't know if I'll be able to follow your level of knowledge. I'll let you know the results of the tests. Thanks
My dedication is not so much specifc to your issue, though i am glad to help. I have been looking for a more simple solution for DMX out on the ESP32 and i have seem to have found it. For the Giga it seems that this should also result in a solution that may work and i am naturally curious of course.
So anyway, to make the Example sketch compile there is first the modification of the sketch itself, and then several modifications to SparkFunDMX.cpp. If the break generation with switching baud-rates doesn't work we can try the pinMode() method that conceptinetics.h uses, to see if that does work.
Ok so i got it to compile for the Giga at least, but i did have to make some more small modifications to the library files. I decided to rename th .h & .cpp files in the SRC and make a separte folder to store them in as not to interfere with the ESP32 code since i could not find a macor that signifies the architecture.
so the ino file now looks like this
// Inlcude DMX library
#include <SparkFunDMXGiga.h> // this is what i called the .h & .cpp files now
#define RX2 34
#define TX2 32
// Create DMX object
SparkFunDMX dmx;
#define dmxSerial Serial2 // this also works on an esp32
// Enable pin for DMX shield (Free pin on Thing Plus or Feather pinout)
uint8_t enPin = 20;
// Number of DMX channels, can be up tp 512
uint16_t numChannels = 512;
// Create a counter as example data
uint8_t counter = 0;
void setup() {
Serial.begin(115200);
Serial.println("SparkFun DMX Example 1 - Output");
// Begin DMX serial port
dmxSerial.begin(DMX_BAUD, DMX_FORMAT);
// Begin DMX driver
dmx.begin(dmxSerial, enPin, numChannels);
// Set communicaiton direction, which can be changed on the fly as needed
dmx.setComDir(DMX_WRITE_DIR);
Serial.println("DMX initialized!");
}
void loop() {
static uint8_t shade = 0;
static uint8_t count = 0;
static uint32_t lastUpdate = micros();
if (micros() - lastUpdate > 22900) {
lastUpdate = micros();
dmx.update(); // so update() must not be called before the frame send has been completed
// or even the break may fail.
count++;
if (!count) {
shade++;
shade = shade % 3;
}
for (uint8_t i = 1; i < 46; i++) {
if (i % 3 == shade) dmx.writeByte(count, i);
else dmx.writeByte(0, i);
}
}
}
and the .h file (SparkFunDMXGiga.h)
#ifndef SparkFunDMXGiga_h
#define SparkFunDMXGiga_h
#include <Arduino.h>
#include <HardwareSerial.h>
// DMX supports up to 512 channels, plus 1 for channel 0
#define DMX_MAX_CHANNELS 513
// DMX messages are started with a break signal of at least 88us
#define DMX_BREAK_DURATION_MICROS 136 // 88
// DMX operates at 250kbps with 2 parity bits
#define DMX_BAUD 250000
#define DMX_FORMAT SERIAL_8N2
// Macros for read or write direcion, DMX can't send and transmit at same time
#define DMX_WRITE_DIR 0
#define DMX_READ_DIR 1
class SparkFunDMX
{
public:
/// @brief Begins DMX class
/// @param port Serial port for communication. This library will not
/// begin the serial port, that must be done before calling dmx.begin()
/// @param enPin Enable pin connected to bridge chip, used for direction
/// @param numChannels Number of DMX channels, 512 max
static void begin(HardwareSerial& port, uint8_t enPin, uint16_t numChannels);
/// @brief Set communication direction, either read or write
/// @param comDir Either DMX_WRITE_DIR or DMX_READ_DIR
static void setComDir(bool comDir);
/// @brief Copy data from a provided byte buffer
/// @param data Buffer with data to be sent
/// @param numBytes Number of bytes to copy from buffer
/// @param startChannel Channel to start copying data to
static void writeBytes(uint8_t* data, uint16_t numBytes, uint16_t startChannel = 1);
/// @brief Copy a single byte to a specified channel
/// @param data Byte to copy
/// @param channel Channel to copy data to
static void writeByte(uint8_t data, uint16_t channel);
/// @brief Copy data to a provided byte buffer
/// @param data Buffer with data to be read
/// @param numBytes Number of bytes to copy to buffer
/// @param startChannel Channel to start copying data from
static void readBytes(uint8_t* data, uint16_t numBytes, uint16_t startChannel = 1);
/// @brief Copy a single byte from a specified channel
/// @param channel Channel to copy data from
/// @return Byte from specified channel
static uint8_t readByte(uint16_t channel);
/// @brief When reading, returns whether data has been received
/// @return True if data is available, else false
static bool dataAvailable();
/// @brief When in read mode, will check to see if new data is available
/// and ready to be read. When in write mode, will actually send out data
/// @return True if successful, else false
static bool update();
private:
// Member variables
static HardwareSerial* _dmxSerial;
static uint8_t _dmxBuffer[DMX_MAX_CHANNELS];
static uint16_t _numChannels;
static uint8_t _enPin;
static bool _comDir;
static bool _dataAvailable;
};
#endif
and the .cpp (SparkFunDMXGiga.cpp)
#include "SparkFunDMXGiga.h"
// Static member definitions and initial values
HardwareSerial* SparkFunDMX::_dmxSerial = nullptr;
uint8_t SparkFunDMX::_dmxBuffer[DMX_MAX_CHANNELS];
uint16_t SparkFunDMX::_numChannels = 1;
uint8_t SparkFunDMX::_enPin = 255;
bool SparkFunDMX::_comDir = DMX_READ_DIR;
bool SparkFunDMX::_dataAvailable = false;
void SparkFunDMX::begin(HardwareSerial& port, uint8_t enPin, uint16_t numChannels)
{
// Store serial stream port
_dmxSerial = &port;
// Store enable pin
_enPin = enPin;
// Store number of requested channels, plus 1 for channel 0
_numChannels = numChannels + 1;
// Ensure number of channels is not above the limit
if(_numChannels > DMX_MAX_CHANNELS)
_numChannels = DMX_MAX_CHANNELS;
// Configure enable pin, default to reading
pinMode(_enPin, OUTPUT);
setComDir(DMX_READ_DIR);
}
void SparkFunDMX::setComDir(bool comDir)
{
// No need to do anything if this direction is already set
if(_comDir == comDir)
return;
// Store communication direction
_comDir = comDir;
// Flush serial buffer contents
_dmxSerial->flush();
if(comDir == DMX_WRITE_DIR)
{
// Enable output
digitalWrite(_enPin, HIGH);
}
else // DMX_READ_DIR
{
// Disable output
digitalWrite(_enPin, LOW);
}
}
void SparkFunDMX::writeBytes(uint8_t* data, uint16_t numBytes, uint16_t startChannel)
{
// Copy data into buffer
uint8_t* startPtr = _dmxBuffer + startChannel;
memcpy(startPtr, data, numBytes);
}
void SparkFunDMX::writeByte(uint8_t data, uint16_t channel)
{
// Store data
_dmxBuffer[channel] = data;
}
void SparkFunDMX::readBytes(uint8_t* data, uint16_t numBytes, uint16_t startChannel)
{
// Clear flag, this is now old data
_dataAvailable = false;
// Copy data into buffer
uint8_t* startPtr = _dmxBuffer + startChannel;
memcpy(data, startPtr, numBytes);
}
uint8_t SparkFunDMX::readByte(uint16_t channel)
{
// Clear flag, this is now old data
_dataAvailable = false;
// Return requested data
return _dmxBuffer[channel];
}
bool SparkFunDMX::dataAvailable()
{
return _dataAvailable;
}
bool SparkFunDMX::update()
{
if(_comDir == DMX_WRITE_DIR)
{
// We need to send a break signal to indicate the start of the message.
// Arduino doesn't really have a way to actually send a break signal, so
// this is a hacky solution: the baud rate is reduced such that sending
// a zero byte creates a sufficiently long pulse to simulate a break
// Reduce baud rate
uint32_t breakBaud = 1000000 * 8 / DMX_BREAK_DURATION_MICROS;
_dmxSerial->flush(); // how about we add this here ??
//_dmxSerial->updateBaudRate(breakBaud);
_dmxSerial->begin(breakBaud, DMX_FORMAT);
// Send a zero at this new baud rate
_dmxSerial->write((uint8_t) 0);
_dmxSerial->flush();
// Return baud rate to original value
//_dmxSerial->updateBaudRate(DMX_BAUD);
_dmxSerial->begin(DMX_BAUD, DMX_FORMAT);
// Send message
_dmxSerial->write(_dmxBuffer, _numChannels);
// Success
return true;
}
// else _comDir == DMX_READ_DIR
// Check if we've received the amount of data expected, +1 for break signal
else if(_dmxSerial->available() >= (_numChannels + 1))
{
// We need to detect a break signal indicating the start of the message.
// Arduino doesn't really have a way to actually read a break signal, it
// will instead appear as an extra zero byte at the start. This has the
// risk of not synchronizing properly, but it should be resolved by
// calling update() freuently enough (ie. faster than messages are sent)
// and flushing the RX buffer if not synced. We can do one other check:
// after the break signal, channel 0 is sent, which should always have a
// value of zero
// Read out break signal (hopefully!) and peek at channel 0 (hopefully!)
if((_dmxSerial->read() != 0) || (_dmxSerial->peek() != 0))
{
// If we get here, then we're not synced properly. We can try to
// flush out all the data and hope the next time is synced
_dmxSerial->flush();
return false;
}
// We're probably synced! Read data into buffer
// i've commebted this out since there is no 'block-read' function available on the Giga
// since we don't use the reception at all it won't matter as yet, but otherwise
// it will be required to read indivdual bytes '_numChannels' times to do this
// (btw i will check in the esp32 core to see if it is a blocking function depending on those bytes
// being available to do this)
//_dmxSerial->read(_dmxBuffer, _numChannels);
// Set flag indicating we have new data
_dataAvailable = true;
// Success
return true;
}
// Something went wrong (most likely haven't received all the bytes yet)
return false;
}
n.b. i use notepad++ to edit the .cpp & .h files. it's a great editor for that kind of stuff.