hello I'm making a device that can send receive midi with arduino on rx/tx and it works.
Dmx out also work on it's own on rx2/tx2.
But when i want to make them both in one code I need to change my baudRate while i'm running the code it works but with one issue few false midi information are created when i change my baudrate if you have a solution thanks a lot (it drive me crazy)
Well i never managed to get a stable DMX out using the UART in combination with WiFi, but if you managed, i guess the best solution would be to use TX1 (not TX2, because that is actually just TX0 but routed to different pins.)
I create DMX out on an ESP using I2S with makuna Neopixelbus library, which is 100% stable DMX out, but that uses GPIO 3 as an output pin, which is also RX0, so i guess initiate Serial.begin(31250), followed by Serial.swap() to use midi on 13 & 15, and use GPIO 3 as the I2S output.
keep in mind there is still an error in the DMX output class.
class NeoEsp8266I2sDmx512SpeedBase
{
public:
// 4.2 us bit send, 250Kbps
static const uint32_t I2sClockDivisor = 21; // 0-63
static const uint32_t I2sBaseClockDivisor = 32; // 0-63
static const uint32_t ByteSendTimeUs = 47; // us it takes to send a single pixel element
static const uint32_t MtbpUs = 100; // min 88
// DMX requires the first slot to be zero
static const size_t HeaderSize = 1;
};
for 250Kbps the bit length is 4us (not 4.2) which means it should be :
static const uint32_t I2sClockDivisor = 20;
static const uint32_t ByteSendTimeUs = 44; // less important.
I have already asked the author to correct this, but he must be a busy man.
See the issue
Oh yes if you do decide to use this method, consider donating for the excellent work in this library.
I use DMXaruino.h it work pretty well but thanks !
It uses the UART, which you can switch to UART1 (GPIO2) but when you test it in combination with WiFi you will find that some of the frames sent get interrupted triggering a reset at the receiving end causing an address miss-match, but if you don't use wifi you will not have any issue.
i use udp packet to send my dmx it has never struggle unless i spam midi in information too
For sending udp packages you don't need the UART at all. You are going to have the share "the complete code" within code-tags to illustrate what you are trying to do and how.
I managed to make it work with some little dmx latency my Serial problem has been resolved by SoftwareSerial.h/cpp (31 250 baud), and in parallele arduinoDMX.h/cpp (250 000 baud)
here the code hope it help someone( so it MIDI in/out with a note_on/off tab sent every sec + DMX out ):
#include <ArduinoRS485.h> // the ArduinoDMX library depends on ArduinoRS485
#include <ArduinoDMX.h>
#include <Arduino.h>
#include <ESP8266WebServer.h>
#include <OSCMessage.h>
#include <WiFiUDP.h>
#include <OSCBundle.h>
#include <SoftwareSerial.h>
#define MIDI_NOTE_OFF 0x80
#define MIDI_NOTE_ON 0x90
#define MIDI_POLY_AFTER 0xA0
#define MIDI_CTRL_CHANGE 0xB0
#define MIDI_PROG_CHANGE 0xC0
#define MIDI_CHANEL_AFTER 0xD0
#define MIDI_PITCH_BEND 0xE0
#define MIDI_SYS_EXCLUSIVE 0xF0
//Ne rien faire
#define ATT_ENTETE 0
//NUMERO
#define NUM_NOTE_OFF 1
#define NUM_NOTE_ON 2
#define NUM_POLY_AFTER 3
#define NUM_CTRL_CHANGE 4
#define NUM_PROG_CHANGE 5
#define NUM_CHANEL_AFTER 6
#define NUM_PITCH_BEND 7
#define NUM_SYS_EXCLUSIVE 8
//VALEUR
#define VAL_NOTE_OFF 10
#define VAL_NOTE_ON 20
#define VAL_POLY_AFTER 30
#define VAL_CTRL_CHANGE 40
#define VAL_PROG_CHANGE 50
#define VAL_CHANEL_AFTER 60
#define VAL_PITCH_BEND 70
#define VAL_SYS_EXCLUSIVE 80
SoftwareSerial mySerial = SoftwareSerial(13,15);
unsigned int ch; //copie la data recu
unsigned int mem_no ; //copie du NUMERO recu
unsigned int running_status_out; //si on a besoin d'actualiser le TYPE
unsigned int running_status_no; //memo du TYPE recu
unsigned int mode_rx_no;//pour la recep serie normale, modes de fonctionnement
int delayMIDI;
int midiselec;
const int universeSize = 512;
const char* SSID = "wifizcis_loc";
const char* PASSWORD = "zdetestelewifi";
WiFiUDP Udp; //initialisation de la paserelle Udp
IPAddress IP(192,168,1,98);
int dmxtab[512];
int timer;
struct midiPiano{
bool used;
bool tabmidi[128];
};
midiPiano tab[16];
midiPiano tabRecep[16];
void init_midi_image(){
for(int i= 0; i <16 ; i++){
tab[i].used=false;
tabRecep[i].used=false;
for(int j= 0; j <128 ; j++){
tab[i].tabmidi[j]=false;
tabRecep[i].tabmidi[j]=false;
}
}
}
void send_midi_image(){
for(int i= 0; i <16 ; i++){
if(tab[i].used==true){
char entete[7]="/o/";
char str[3];
sprintf(str, "%d", i);
strcat(entete,str);
OSCMessage msgOut(entete); // En-tête de la réception
for(int j= 0; j <128 ; j++){
if(tab[i].tabmidi[j]==true)
msgOut.add("1");
else
msgOut.add("0");
}
Udp.beginPacket(IP, 7400);// Ecriture du msg a l'ip et port voulu
msgOut.send(Udp); // Envoie du msg Udp
Udp.endPacket();
msgOut.empty();
}
}
}
void check_tab(OSCMessage &msg, int i){
for(int j = 0 ; j<128 ; j++){
if(tabRecep[midiselec].tabmidi[j]==false){
if(msg.getInt(j)==1){
tabRecep[midiselec].tabmidi[j]=true;
Serial.write(MIDI_NOTE_ON+midiselec);
Serial.write(j);
Serial.write(64);
}
}
else{
if(msg.getInt(j)==0){
tabRecep[midiselec].tabmidi[j]=false;
Serial.write(MIDI_NOTE_OFF+midiselec);
Serial.write(j);
Serial.write(0);
}
}
}
}
void fix_midi_image(int canal,int pitch,bool value){
tab[canal].used=true;
tab[canal].tabmidi[pitch]=value;
}
void fix_midi_image_recep(int canal,int pitch,bool value){
tabRecep[canal].used=true;
tabRecep[canal].tabmidi[pitch]=value;
}
void setup() {
DMX.begin(universeSize);
pinMode(13,INPUT);
pinMode(15,OUTPUT);
mySerial.begin(31250); // Vitesse du protocole MIDI
WiFi.begin(SSID, PASSWORD);
Udp.begin(8888);
ch=0;
init_midi_image();
delayMIDI=millis()+1000;
}
void loop() {
envoie_dmx();
envoie_serie();
receiveOSC();
if(delayMIDI<millis() || delayMIDI-1000>millis()){
send_midi_image();
delayMIDI=millis()+1000;
}
}
void envoie_dmx(){
if(timer<millis() || timer-44>millis() ){
timer=44+millis();
DMX.beginTransmission();
for(int p= 0 ; p<512 ;p++){
if(dmxtab[p]!=0) DMX.write(p+1,dmxtab[p]);
}
DMX.endTransmission();
}
}
void receiveOSC(){
OSCMessage msgIN;
int size;
if((size = Udp.parsePacket())>0){
while(size--){
msgIN.fill(Udp.read());
}
if(!msgIN.hasError()){
msgIN.route("/noteon",noteOn);
msgIN.route("/noteoff",noteOff);
msgIN.route("/progchange",progChange);
msgIN.route("/chanelafter",chanelAfter);
msgIN.route("/pitchbend",pitchBend);
msgIN.route("/polyafter",polyAfter);
msgIN.route("/ctrl",ctrl);
msgIN.route("/dmx",dmx);
if(msgIN.fullMatch("/0",2)) midiselec=0;
if(msgIN.fullMatch("/1",2)) midiselec=1;
if(msgIN.fullMatch("/2",2)) midiselec=2;
if(msgIN.fullMatch("/3",2)) midiselec=3;
if(msgIN.fullMatch("/4",2)) midiselec=4;
if(msgIN.fullMatch("/5",2)) midiselec=5;
if(msgIN.fullMatch("/6",2)) midiselec=6;
if(msgIN.fullMatch("/7",2)) midiselec=7;
if(msgIN.fullMatch("/8",2)) midiselec=8;
if(msgIN.fullMatch("/9",2)) midiselec=9;
if(msgIN.fullMatch("/10",2)) midiselec=10;
if(msgIN.fullMatch("/11",2)) midiselec=11;
if(msgIN.fullMatch("/12",2)) midiselec=12;
if(msgIN.fullMatch("/13",2)) midiselec=13;
if(msgIN.fullMatch("/14",2)) midiselec=14;
if(msgIN.fullMatch("/15",2)) midiselec=15;
msgIN.route("/o",check_tab);
}
}
}
void dmx(OSCMessage &msg, int i){
dmxtab[msg.getInt(0)-1]=msg.getInt(1);
}
void noteOn(OSCMessage &msg, int i){
mySerial.write(MIDI_NOTE_ON+msg.getInt(0));
mySerial.write(msg.getInt(1));
mySerial.write(msg.getInt(2));
fix_midi_image_recep( msg.getInt(0), msg.getInt(1) , msg.getInt(2)==0 ? false : true );
}
void noteOff(OSCMessage &msg, int i){
mySerial.write(MIDI_NOTE_OFF+msg.getInt(0));
mySerial.write(msg.getInt(1));
mySerial.write(0);
fix_midi_image_recep( msg.getInt(0) , msg.getInt(1) , false );
}
void progChange(OSCMessage &msg, int i){
mySerial.write(MIDI_PROG_CHANGE+msg.getInt(0));
mySerial.write(msg.getInt(1));
}
void chanelAfter(OSCMessage &msg, int i){
mySerial.write(MIDI_CHANEL_AFTER+msg.getInt(0));
mySerial.write(msg.getInt(1));
}
void pitchBend(OSCMessage &msg, int i){
mySerial.write(MIDI_PITCH_BEND+msg.getInt(0));
mySerial.write(msg.getInt(1));
mySerial.write(msg.getInt(2));
}
void polyAfter(OSCMessage &msg, int i){
mySerial.write(MIDI_POLY_AFTER+msg.getInt(0));
mySerial.write(msg.getInt(1));
mySerial.write(msg.getInt(2));
}
void ctrl(OSCMessage &msg, int i){
mySerial.write(MIDI_CTRL_CHANGE+msg.getInt(0));
mySerial.write(msg.getInt(1));
mySerial.write(msg.getInt(2));
}
void EnvoieMidiUdp(char* type, int canal, int pitch, int velocity){
OSCMessage msgOut(type ); // En-tête de la réception
msgOut.add(canal);
msgOut.add(pitch);
msgOut.add(velocity);
Udp.beginPacket(IP, 7400);// Ecriture du msg a l'ip et port voulu
msgOut.send(Udp);// Envoie du msg Udp
Udp.endPacket();
msgOut.empty();
}
void envoie_serie()
{
if(mySerial.available()>0){
ch=mySerial.read();
if (ch&0x80 )// (TYPE) l octet recu est un entete de message midi
{ sent=false;
switch (ch&0xF0) // aiguillage selon le (TYPE)
{
//Virer les cases non utilisés
case MIDI_NOTE_OFF : // 0 0x80
//on part du principe que tout ce qui arrive après reste du meme TYPE
running_status_no = ch&0x0f;
// changer mode_rx_no=X; en fonction de si c'est pertinent pour le code
mode_rx_no = NUM_NOTE_OFF;
break;
case MIDI_NOTE_ON : // 1 0x90
running_status_no = ch&0x0f;
mode_rx_no = NUM_NOTE_ON;
break;
case MIDI_POLY_AFTER : // 2 0xA0
running_status_no = ch&0x0f;
mode_rx_no = NUM_POLY_AFTER;
break;
case MIDI_CTRL_CHANGE : // 3 0xB0
running_status_no = ch&0x0f;
mode_rx_no = NUM_CTRL_CHANGE;
break;
case MIDI_PROG_CHANGE : // 4 0xC0
running_status_no = ch&0x0f;
mode_rx_no = NUM_PROG_CHANGE;
break;
case MIDI_CHANEL_AFTER : // 5 0xD0
running_status_no = ch&0x0f;
mode_rx_no = NUM_CHANEL_AFTER;
break;
case MIDI_PITCH_BEND : // 6 0xE0
running_status_no = ch&0x0f;
mode_rx_no = NUM_PITCH_BEND;
break;
case MIDI_SYS_EXCLUSIVE: // 7 0xF0
//sysex respect pas la structure classique, à gérer au cas par cas
if (ch<0xF8) {mode_rx_no = ATT_ENTETE;}//les real time n affecteront rien
break;
default :
mode_rx_no = ATT_ENTETE;
break;
}
}
else // l'octet recu est une donnee
{
switch(mode_rx_no)
{
case ATT_ENTETE: //attente d un octet d'entete midi
mode_rx_no = ATT_ENTETE;
break;
case NUM_NOTE_OFF:
mem_no = ch; // memorise le NUMERO du message
mode_rx_no = VAL_NOTE_OFF;
break;
case NUM_NOTE_ON:
mem_no = ch;
mode_rx_no = VAL_NOTE_ON;
break;
case NUM_POLY_AFTER:
mem_no = ch;
mode_rx_no = VAL_POLY_AFTER;
break;
case NUM_CTRL_CHANGE:
mem_no = ch;
mode_rx_no = VAL_CTRL_CHANGE;
break;
case NUM_PROG_CHANGE:
mem_no = ch;
mode_rx_no = VAL_PROG_CHANGE;
EnvoieMidiUdp("/progchange", running_status_no, mem_no, 0);
break;
case NUM_CHANEL_AFTER:
mem_no = ch;
mode_rx_no = VAL_CHANEL_AFTER;
EnvoieMidiUdp("/chanelafter", running_status_no, mem_no, 0);
break;
case NUM_PITCH_BEND:
mem_no = ch;
mode_rx_no = VAL_PITCH_BEND;
break;
case NUM_SYS_EXCLUSIVE:
mem_no = ch;
mode_rx_no = VAL_SYS_EXCLUSIVE;
break;
//-------------------------------------------------------
case VAL_NOTE_OFF:
mode_rx_no = NUM_NOTE_OFF;
EnvoieMidiUdp("/noteoff", running_status_no, mem_no, 0);
fix_midi_image(running_status_no,mem_no,false);
break;
case VAL_NOTE_ON:
mode_rx_no = NUM_NOTE_ON;
if(ch!=0){
EnvoieMidiUdp("/noteon", running_status_no, mem_no, ch);
fix_midi_image(running_status_no,mem_no,true);
}
else{
EnvoieMidiUdp("/noteoff", running_status_no, mem_no, ch);
fix_midi_image(running_status_no,mem_no,false);
}
break;
case VAL_POLY_AFTER:
mode_rx_no = NUM_POLY_AFTER;
EnvoieMidiUdp("/polyafter", running_status_no, mem_no, ch);
break;
case VAL_CTRL_CHANGE:
mode_rx_no = NUM_CTRL_CHANGE;
EnvoieMidiUdp("/ctrl", running_status_no, mem_no, ch);
break;
case VAL_PITCH_BEND:
mode_rx_no = NUM_PITCH_BEND;
EnvoieMidiUdp("/pitchbend", running_status_no, mem_no, ch);
break;
//juste pour faire beau, sysex respect pas la structure classique, à gérer au cas par cas
case VAL_SYS_EXCLUSIVE:
mode_rx_no = NUM_SYS_EXCLUSIVE;
break;
default : // en cas d erreurs
mode_rx_no = ATT_ENTETE;
break;
}
}
}
}
Yes well, i still think the best would be to use UART1 to send DMX, but the library you use is actually meant for a specific shield and doesn't have an easy way for doing this. I always get glitches when use UART for DMX in combination with Wifi (mind you mainly when using it as an AP, but also when creating a webserver. So if you doi get that, you will know where to look.
You think if I use on my esp as an AP i'll have some issue ? that's the next step...
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.