problem using MIDI library and serial monitor at the same time

Status
Not open for further replies.

kat

Member
Hi all,

I think I am misunderstanding some basic things about Serial communications. What I am trying to do is use the MIDI library (this one: https://github.com/FortySevenEffects/arduino_midi_library) for communication via DIN input using Serial2 pins on the Teensy. I am trying to do this in a library that I am writing. My problem is that the 'begin' method seems to interfere with the settings for serial monitoring, such that I am no longer able to have diagnostic printouts.

Note that I have verified my hardware setup and have verified that I can use the MIDI library to receive input from the hardware.

Here is code (sketch and .h and .cpp files invoked) that will reproduce the issue. I am using Teensy 3.2.

Sketch:
Code:
#include <test.h>

test t;

void setup() {
  Serial.begin(115200);

}

void loop() {
   Serial.println("serial printout from sketch");
   t.dosomething();
   delay(1000);

}

Here's a .h file for a dummy library just to showcase the problem:
Code:
//  test.h

#include <MIDI.h>

class test
{

public:
    test(void);
    void dosomething(void);
    //this is the MIDI interface that I want... Serial2 pins:
    midi::MidiInterface<HardwareSerial> THISMIDI = midi::MidiInterface<HardwareSerial>((HardwareSerial&)Serial2);

};

Here is the .cpp file.
Code:
#include "test.h"

test::test(void){

//the line below, if uncommented, will cause Serial monitoring to cease    
//    THISMIDI.begin(MIDI_CHANNEL_OMNI);
    
}

void test::dosomething(void){
    Serial.println("printout from within test method");
}

If I run this code as it is shown above, I get my serial printouts:
serial printout from sketch
printout from within test method
(repeated infinitely)

If I uncomment the line "THISMIDI.begin(MIDI_CHANNEL_OMNI);" that begins the MIDI monitoring, serial monitoring also ceases and I am no longer able to write messages to the serial monitor from within my sketch or from within the class.

I am relatively new to Arduino and Teensy programming and I think there are some things I am not understanding about how the serial interfaces work and how default interfaces are handled when invoked from within a library. So maybe I am just doing something stupid here? Help?
 
This is in the context of a larger hardware development project which is intended to provide an interface between MIDI devices and other things. So working with DIN MIDI I/O is one of the key points of the effort.

If the MIDI interface is not compatible with serial monitoring, though, it seriously limits the potential for this code. So... I do hope somebody has some insights about what's causing this problem!
 
Basically, the MIDI functionality should be compatible with serial monitoring, especially since all Teensy 3.x MCUs have two or more independent UARTs. I didn’t yet find the time to look into the library code, but I’m sure that the cause of the problem is there and how it initializes the Serial ports, knowing that it hat initially be written for Arduino which had only one single UART...
 
Here is a PJRC page showing DIN port MIDI over Serial1 : https://www.pjrc.com/teensy/td_libs_MIDI.html

(if I read correctly) That shows a Teensy 2 using its Serial1 pins 7 and 8 wired to MIDI DIN's - one IN and one OUT. I have no idea if that is a 3.3V T_3.2 capable interface or if it runs at 5V and extra work is needed? There may be better pages on PJRC or the FORUM.

You can learn about Teensy Serial USB versus Serial# [ 1 or 2 or 3 ] for T_3.2 on this page: https://www.pjrc.com/teensy/td_uart.html

From what I see the USB would be left as 'Type Serial' for debug/status messages and then the library above would be set up for use on Serial1 pins of the T_3.2?
 
The OP wanted to use Serial2 and I'm just not sure if that library (which was initially written for the Arduino with Serial fixed on pins 0 and 1) supports that. Just will have no time before the holidays to look into that... Serial1 should work by default.
 
The OP wanted to use Serial2 and I'm just not sure if that library (which was initially written for the Arduino with Serial fixed on pins 0 and 1) supports that. Just will have no time before the holidays to look into that... Serial1 should work by default.

Missed that - looks like is can use Serial2:

/*! \brief Create an instance of the library attached to a serial port.
You can use HardwareSerial or SoftwareSerial for the serial port.
Example: MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midi2);
Then call midi2.begin(), midi2.read() etc..
*/

#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
midi::MidiInterface<Type> Name((Type&)SerialPort);
 
Thanks for looking at this. Yes, I want to use Serial2, but the same problem exists if I instantiate using Serial1. The pinout needs are a constraint of the project - we have a hardware interface uses up basically every Teensy pin, and the only thing I have available for DIN MIDI at this point is the alternate Serial 2 pins. But again, I don't think this has anything to do (directly anyway) with the problem that I am seeing. This kind of thing works just fine in a sketch:

Code:
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI);

void setup() {

  Serial.begin(57600);
  Serial.println("MIDI Input Test");

  Serial2.setRX(26);
  MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop() {
  if (MIDI.read()){
    Serial.println("got something");   
  }
}

Thus, the MIDI library is working just fine with the Serial2 interface. My problem only arises when I try to move some of this functionality into a library, building a class to represent my hardware device with all of its pin associations. The previous example code is just a minimal example of the conflict that arises when I want to use serial monitoring and work with the MIDI interface within a class. I think the problem has to do with compile-time mechanics - something is different about how the code ends up compiled when it is invoked in a sketch vs. included in a library. But this is beyond my understanding with Arduino code, at this point!
 
My problem only arises when I try to move some of this functionality into a library, building a class to represent my hardware...

This may be stating the overly obviously, but showing us a program that does work isn't going to enable any of us to help you figure out why your unseen C++ class code doesn't work.

But maybe it'll help if I mention a very similar question came up a month or two ago, involving a C++ class for a giant MIDI controller project, which abstracted the serial port. Writing to the serial port in loop() worked, but writing to it in their class didn't. It turned out to be a simple mistunderstanding/mistake of using "static" within their C++ class. The net result was they has two completely different copies of the serial object. So their code within the C++ class was writing their data into a completely separate instance of the serial port, but of course the hardware interrupts were only servicing the original one. Their data just sat neglected in the other copy of the serial object they has unwittingly created.

C++ syntax and semantics can be tricky like that. We have the "Forum Rule" for exact this reason. When you post the complete code (or a small version, but still complete and confirmed to reproduce the problem), we can usually help.
 
Yep, complete (not) working code example is at the top (original post) to reproduce this problem. In that example, the class is very simple.
 
Usually you can't call serial begin() from your constructor, if your object is allocated global / static. The common solution is to create your own begin() function, and use it to call any other begin() functions for serial and other hardware.

As a general rule, for classes that will be allocated as global / static, you need to restrict your actions in your constructor to only your own class.

To understand why, in a nutshell when your object and other objects it uses are all created as global / static, the other objects you try to use within your constructor may not yet have had their constructors run. If they use virtual functions, their vtables may not yet be initialized. When your constructor tries to access an instance of another class, that instance may not actually "exist" yet. Undefined things happen, usually on ARM chips resulting in a memory fault which for all practical purpose means your code crashes.

This is a well known C++ problem which is called the static initialization order fiasco. You can find lots of info online if you search for that term. But ultimately the answers all boil down to a few solutions, and the one that's used in the Arduino world is a begin() function, and limiting your constructor to only initializing stuff within your own class.
 
Ah, that's helpful. I was wondering about the common use of begin() functions for arduino objects, and whether I needed to do that too. Not knowing the whole 'fiasco' I couldn't quite figure out why it would make a difference, but I see the point about the timing of constructor calls. I will see if using a begin() function in my own class fixes things, and will post a working example assuming it does (in case anyone else reads this thread later and wants to do something similar). Thank you for your time!
 
Indeed, this works. For the benefit of future readers, here's a version of the sketch and test library that functions properly. Lesson learned: do not call begin() functions in constructors.

Sketch:
Code:
#include <test.h>

test t;

void setup() {
  Serial.begin(115200);

}

void loop() {
   Serial.println("serial printout from sketch");
   t.dosomething();
   delay(1000);

}

.h file:
Code:
/
/  test.h

#include <MIDI.h>

class test
{

public:
    test(void);
    void begin(void);
    void dosomething(void);
    
    midi::MidiInterface<HardwareSerial> THISMIDI = midi::MidiInterface<HardwareSerial>((HardwareSerial&)Serial2);


};

.cpp file:
Code:
#include "test.h"


test::test(void){
   //constructor stuff limited to this class alone would go here
}

void test::begin(){
    //call other begin functions here
    THISMIDI.begin(MIDI_CHANNEL_OMNI);

}

void test::dosomething(void){
    Serial.println("printout from within test method");
}

This is the kind of thing - where the details of the context don't matter that much but it's a more general issue - that is hard to debug without help, so thanks for the assistance!
 
Status
Not open for further replies.
Back
Top