Giter Site home page Giter Site logo

Comments (37)

RobertoHE avatar RobertoHE commented on September 18, 2024 1

For this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B), receive() had to add to the buffer the

The first byte (0x08) is not valid, bit 7 needs to be 1 (I guess a typo) (the message from the logs had the bit set to 1)

It is true. First byte is wrong in my example. It is possible that when I copied this I accidentally changed 0x80 to 0x08. First bit always must be 1 in high timeStamp byte

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024 1

You are right, it is not neccessary.

0xF0s have 1 byte lenght, except SysEx.

if (sysExContinuation)
midiType = SystemExclusive;

Is this code needed? Looks like it can be removed. The switch below does not test for SystemExclusive

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024 1

* It recommend not use runningStatus by default. Only use if parser accepts runningStatus and your application has a so high transmission rate.
*/
//#define RUNNING_ENABLE

You recommend not using RUNNING_ENABLE - yet the underlying parser does support runningStatus for incoming messages and runs fine with the RUNNING_ENABLE enabled or disabled (running msvc examples renders the same result). I kinda like the efficiency that runningStatus brings.

Based on the current examples in BLEMIDI_Sim.h, i recommend making RUNNING_ENABLE permanent. WDYT @RobertoHE ?

I only have tested the parser with default settings of serial parser of main midi class. I haven't have time for check if any other options of setting class would interference with runningStatus mode.

If any setting option not interferences with it, runningStatus enable is prefered because it uses less resorces.

Other option is change #defines by if(Settings::option). This will add some computer cycles to parser but it would be transparent to end user or end application.

Would you check it in paralle?

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

I have seen the same bug too.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

DAW's tend to send a lot of MIDI messages, could you add a Serial.println(byte); at L108 in BLEMIDI_Transport.h (and enable logging in setup) and send the dump

unsigned available()
{
uint8_t byte;
auto success = mBleClass.available(&byte);
if (!success) return mRxIndex;
mRxBuffer[mRxIndex++] = byte;

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

I have seen the same bug too.

do you have the exact same setup @RobertoHE ?

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi @lathoub

I apologize for the lenght of this message.

I have seen the bug in 2 differents ways: the first one with ServerNimBLE, and the second one with my Client proposal (pull request).

In the server Mode (BleNimble), I used the following setup:

Server (ESP32 ServerNimBLE):
-board = esp32dev
-framework = VSCode/PlatformIO (arduino)
-Code: Basic example with noteOn, noteOff and AffterTouch callback handlers. Loop() only contains Midi.read().

Client:
-DAWCubase
-Win10
-BLE (using loopMidi in the Middle)
-MidiController: Roland AX-EDGE by USB (DAW in the middle) or Casio Pravia PX-560

I generated "Midi Chords" (some notes simultaneously) and some afterTouch automatizations in channel 5 using DAW while playing MidiController generating notes, pitch-bends and aftertouchs messages in channels 1, 2 and 3. The server was in OMNI_CHANNEL mode (receiving 1 ,2 ,3 and 5 channels).
I saw sometimes that same behavour that @aphleps had explained before. I received some notes over 127 pitch or over 127 velocity. I didn't save any logs, sorry. I tried to increment the maxBufferSize, but that did not solve anything (The buffer was never seen overflowed in traces).

In the second setup I used:
Client (ESP - clientNimble):
-board = esp32dev
-framework = VSCode/PlatformIO (arduino)
-Code: Custom code that converts received Midi notes in light patrons using FastLed. Midi.read() is called in a FreeRTOS task, like client example.

Server (AX-EDGE)
-MidiController: AX-EDGE over BLE

This setup is "a little weird" because this client is not in this repo yet, but I hope that you might accept it in the future if it works properly (I am still working on it, but I have seen some lines that I want to change).
The "client version" uses the same midiTransport template and the same Midi decoder than Server Version.

I generated notes, pitch-bends and aftertouchs messages in channels 1, 2 and 3 when I played MidiController (server in this case with BLE notify characteristic) and I could see the NoteOn events using the callback handler. I received in some ocassions weird notesOn events like @aphleps and, at other times, NoteOn callbacks stopped working during severals seconds (between 10s and 90s) but BLE notifications were still received (debug traces were activated and they worked propertly, it still dettected any message generated by MidiController).

I have added some traces in notifyCB(), receive() and read() (and others) functions in Client , Transport and Midi layers and I have seen that BLE notify works fine. NimBLE and notifyCB() still receive incoming data correctly and the data uplinks correctly to the receive() transport layer.

Receive() function adds data to buffer, but I don't know whether it adds correctly F7 (SysEx) type messages.

I think that the problem may appear when SysEx message comes between other midi messages in the same pakage. I used the callback setHandleMessage() and when this error appears MidiMessage callback object is message.type = F0 (system Exclusive) and message.lenght = 128 (max buffer size in my case). After that, a weird noteOn or other type event is reveived. I think that way of extracting data from the package ( while(true){}, lPtr and rPtr) may origine the issue when F7 appear in the middle of the package.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Thanks @RobertoHE

I think that the problem may appear when SysEx message comes between other midi messages in the same pakage. I used the callback setHandleMessage() and when this error appears MidiMessage callback object is message.type = F0 (system Exclusive) and message.lenght = 128 (max buffer size in my case). After that, a weird noteOn or other type event is reveived. I think that way of extracting data from the package ( while(true){}, lPtr and rPtr) may origine the issue when F7 appear in the middle of the package.

Good catch, this might be the cause of the issue. Let me check over the weekend with the specs. (A message dump would be ideal for me to debug)

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi @lathoub

I have to apologize again for the lenght of the message.

I send you a log of one of the errors. In this case, the error is which cause that parser get crashes and it stops to trigger callbacks (second error of the previous message).

I used the second setup (Client Mode) that it was explained in the previous comment.
In this case, I only have sent Midi Messages playing the AX-EDGE (only noteOn, noteOff and afterTouch event types in channels 1, 2, and 3 simultaneously, or triplicated).

In this case, the crash occurred when I was sending a lot of afterTouch events (syntetizer may sends more than 50 events in a second).

I think that BLEMIDI_Transport.receive() has some problems when it receives a second message that arrives while it is processing other one (for example: DAW sends a note and SysEx or other system data in a sort time or, in this case, a lot of affterTouch messages in sort time). Client mode uses a callback that it is trigged automaticly when a notification occurred, and it may cause that two parser may run simultaneously and they may add data at the same time in the RX buffer. I don't sure completly if the bug occurres because the two parser try to add() simultaneously their data in the buffer and they mix the data accidentally, but it may be a possibility.

After the crash bug, some events continued to arrive and parser sended again a event randomly some times until it started to work fine again. I think that the machine of states of parse() suffers when a bad-created message is added to RX buffer and it tries to process it. Until some "bad midis" pass throught the parser and one of them cleans the machine of states, the parser can't trigger any callback. The extra bad-created messages don't trigger error handler, and it isn't normal and it may be a sympthom.

I have tested a little be other MIDI parser in C++ (https://github.com/TheKikGen/midiXparser) that may do the same that received() in less time. I haven't tested it enoughtly, I will continue to investigate it. But unfortunately, it may present the same inconvenient when it tries to insert data in RX buffer ( add() method).

Alternatively, a flag or semaphore may be added in add() and read() to prevent similtaneous read/write or write/write errors.

I show you the traces that I added for "debugging".

When BLE notification is received (notifiCB()) from AX-EDGE, it prints "-New MSG-" and the incoming data in HEX format.

I set MIDI callbacks as follow:

    MIDI.setHandleError([](int8_t fptr)
                         {
                           Serial.println("\n\nError in parser:" + String(fptr) + "\n\r\n");
                         });

    MIDI.setHandleMessage([](const midi::Message<128U> &msg)  //It is triggered by MIDI.read() when 
      {                                                       //MIDI.parser() process correctly a event
      Serial.println("\n### Parser ###");
      Serial.print("Type: "); Serial.print((msg.type),HEX);
      Serial.println();
      Serial.println("Lenght: " + String(msg.length));
      Serial.println("Channel: " + String(msg.channel));
      });

LOGS:
(Bug appear when ###Parser### stops)

[...]

-New MSG-
A8 C0 90 3E 3E C1 91 3E 3E C1 92 3E 3E C2 90 40 3E

-New MSG-
A8 C3 91 40 3E C3 92 40 3E

### Parser ###
Type: 90
Lenght: 1
Channel: 1

### Parser ###
Type: 90
Lenght: 1
Channel: 2

### Parser ###
Type: 90
Lenght: 1
Channel: 3

### Parser ###
Type: 90
Lenght: 1
Channel: 1

### Parser ###
Type: 90
Lenght: 1
Channel: 2

### Parser ###
Type: 90
Lenght: 1
Channel: 3

-New MSG-
A9 99 D0 3 99 D1 3

-New MSG-
A9 9A D2 3 9C D0 8 9C D1 8

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1

-New MSG-
Channel: 2
A9 9D D2 8 9F D0 15 9F D1 15

### Parser ###
Type: D0
Lenght: 1

-New MSG-
A9 A0 D2 15 A2 D0 22 A2 D1 22
Channel: 3

-New MSG-
A9 A3 D2 22 A5 D0 33

### Parser ###
Type: D0
-New MSG-
A9 A5 D1 33 A6 D2 33 A8 D0 42


-New MSG-
A9 A8 D1 42 A9 D2 42 AB D0 57
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

-New MSG-
A9
### Parser ###
Type: D0
Lenght: 1
 AB D1 57 AC D2 57 AE D0 74
Channel: 1

-New MSG-
A9 AE D1 74 AF D2 74 B1 D0 7F B1 D1 7F B2 D2 7F

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

-New MSG-
AE AA 80 40 3E AA 81 40 3E AB 82 40 3E

-New MSG-
AE AC 80 3E 3E AC 81 3E 3E AD 82 3E 3E

### Parser ###
Type: 80
Lenght: 1
Channel: 1

### Parser ###
Type: 80
Lenght: 1
Channel: 2

### Parser ###
Type: 80
Lenght: 1
Channel: 3

### Parser ###
Type: 80
Lenght: 1
Channel: 1

### Parser ###
Type: 80
Lenght: 1
Channel: 2

### Parser ###
Type: 80
Lenght: 1
Channel: 3

-New MSG-
AF DF D0 78 DF D1 78 E0 D2 78

-New MSG-
AF E2 D0 67 E2 D1 67 E3 D2 67

### Parser ###
Type: D0
Lenght: 1
Channel: 1

-New MSG-
AF
### Parser ###
Type: D0
Lenght: 1
 Channel: 2
E5 D0 59 E5 D1 59 E6 D2 59

### Parser ###
Type: D0
Lenght: 1

-New MSG-
AFChannel: 3
 E8 D0 50 E8 D1 50 E9 D2 50

### Parser ###
Type: D0

-New MSG-
AF EB D0 4C EB D1 4C EC D2 4C
Lenght: 1

-New MSG-
AF EE D0 43 EE D1 43 EF D2 43
Channel: 1

-New MSG-
AF F1 D0 40 F1 D1 40 F2 D2 40 

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1

-New MSG-
Channel: 3
AF F4 D0 35 F4 D1 35 F5 D2 35

### Parser ###
Type: D0
Lenght: 1

-New MSG-
AF F7 D0 26 F7 D1 26 F8 D2 26 FA D0 F FA D1 F FB D2 F

-New MSG-
B0 81 D0 3 82 D1 3 82 D2 3
Channel: 1

-New MSG-
B0 83 D0 0 83 D1 0 84 D2 0

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1

-New MSG-
B0Channel: 2
 C8 90 3E 28 C9 91 3E 28 C9 92 3E 28

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

-New MSG-

### Parser ###
Type: D0
Lenght: 1
B0Channel: 2
 CB 90 40 25 CB 91 40 25 CD 92 40 25

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

### Parser ###
Type: D0
Lenght: 1
Channel: 2

### Parser ###
Type: D0
Lenght: 1
Channel: 3

### Parser ###
Type: D0
Lenght: 1
Channel: 1

-New MSG-
B2 BA D0 3 BA D1 3 BB D2 3

-New MSG-
B2 BD D0 8 BE D1 8 BE D2 8

-New MSG-
B2 C0 D0 19 C0 D1 19 C1 D2 19

-New MSG-
B2 C3 D0 2B C3 D1 2B C4 D2 2B

-New MSG-
B2 C6 D0 3A C6 D1 3A C7 D2 3A

-New MSG-
B2 C9 D0 47 C9 D1 47 CA D2 47

-New MSG-
B2 CC D0 54 CC D1 54 CD D2 54

-New MSG-
B2 CF D0 6E CF D1 6E D0 D2 6E

-New MSG-
B2 D2 D0 7F D2 D1 7F D3 D2 7F

-New MSG-
B4 E4 D0 77 E4 D1 77

-New MSG-
B4 E5 D2 77 E7 D0 31 E7 D1 31

-New MSG-
B4 E8 D2 31 EA D0 C EA D1 C

-New MSG-
B4 EB D2 C ED D0 0 ED D1 0

-New MSG-
B4 EE D2 0

-New MSG-
B5 AE 80 40 39 AF 81 40 39 AF 82 40 39

-New MSG-
B5 BB 80 3E 27

-New MSG-
B5 BC 81 3E 27 BC 82 3E 27

-New MSG-
B6 84 90 3E 23 84 91 3E 23 85 92 3E 23

-New MSG-
B6 8E 90 40 2C 8E 91 40 2C 8F 92 40 2C

### Parser ###
Type: F0
Lenght: 128
Channel: 0

-New MSG-
B6 BE D0 3 BE D1 3 BF D2 3

-New MSG-
B6 C1 D0 14 C1 D1 14 C2 D2 14

-New MSG-
B6 C4 D0 3C

-New MSG-
B6 C6 D1 3C C6 D2 3C C7 D0 77 C7 D1 77

-New MSG-
B6 C8 D2 77 CA D0 7F CA D1 7F CB D2 7F

-New MSG-
B7 B7 D0 74 B7 D1 74 B8 D2 74

-New MSG-
B7 BA D0 3E BA D1 3E BB D2 3E

-New MSG-
B7 BD D0 19 BE D1 19 BE D2 19

-New MSG-
B7 C0 D0 5 C0 D1 5 C1 D2 5

-New MSG-
B7 C3 D0 0 C3 D1 0 C4 D2 0

-New MSG-
B7 FE 80 40 57

-New MSG-
B7 FE 81 40 57 FF 82 40 57

-New MSG-
B8 83 80 3E 61 84 81 3E 61

-New MSG-
B8 84 82 3E 61

-New MSG-
B8 C2 80 43 45 C3 81 43 45

-New MSG-
B8 C4 82 43 45 C4 80 47 36 C5 81 47 36 C5 82 47 36

-New MSG-
B8 C6 80 45 4F C6 81 45 4F C7 82 45 4F

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Thanks @RobertoHE , appreciate the long message :-)

I think that BLEMIDI_Transport.receive() has some problems when it receives a second message that arrives while it is processing other one (for example: DAW sends a note and SysEx or other system data in a sort time or, in this case, a lot of affterTouch messages in sort time). Client mode uses a callback that it is trigged automaticly when a notification occurred, and it may cause that two parser may run simultaneously and they may add data at the same time in the RX buffer. I don't sure completly if the bug occurres because the two parser try to add() simultaneously their data in the buffer and they mix the data accidentally, but it may be a possibility.

Does this would mean that ESP32's xQueueReceive (thread 2) and xQueueSend (thread 1) are not fenced and need additional semaphores? (and therefor should work on non-threaded hardware, like a Arduino NANO 33 BLE?)

Or, is it because SysEx messages are intertwined in the normal messages (and would occur on all hardware)?

Where in the above stream are things going wrong?

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi again @lathoub .

The last bug is NOT generated by a SysEx. The data streams only had noteON, noteOFF and afterTouch events. I think that the bug is related to 2 or more multiples writer tasks for the same buffer.

I have analyzed receive() function and I have some questions, because I am newbie in multi-task RTOS.

In receive(), parsed (or accepted) midi package is added to queue byte by byte. For example, a noteOn midi is 3 bytes length and receive() must call 3 times to add() function. In other words, it needs to call 3 differents times to xQueueSend() function.

Add() calls to xQueueSend() and copies only 1 byte to Queue. When xQueueSend() is running, the queue is locked and protected, and that's fine. But when xQueueSend() ends, it creates some little gaps between a add() function and the next one in which Queue is unprotected, doesn't it?

Example of 2 writer threads:

[  ] -> xQueueSend (). queue is locked and protected.
--   -> time between xQueueSend. queue is unprotected.

Thread NotifyCB A: Start |->> Parser ->  Note ON ok   ->  [A0 add] -- [A1 add] -- [A2 add] ->| ends
                                                                               ^    |
                                                                               |    v
Thread NotifyCB B:                  Start |->> Parser ->  Note ON ok   ->  [B0 add] -- [B1 add] -- [B2 add] ->| ends

Expected Buffer : A0 A1 A2 B0 B1 B2
Received Buffer:  A0 A1 B0 A2 B1 B2

I am a beginner in FreeRTOS, so I am not completely sure whether Queue works this way or not.

I think that it may be necessary to call taskENTER_CRITICAL() or vTaskSuspendAll() (or something similar) to protect Queue.

Midi Messages have a variable length and Queue forces to divide messages in bytes to adapt to this variable length. Splitting messages up may mix some packages and it may cause a bug in Midi.read(), which only can process "well-generated" messages.

One inconvenient of Queue is that you only can add fixed size objects to queue, and this doesn't fit well with midi packages. Stream Buffers or Messages Buffers adapt better to variable size packages but they have the inconvenient that they don't have any implicit protection to lock the buffer for 2 o more writer tasks, and they need taskENTER_CRITICAL() or vTaskSuspendAll() again.

Using taskENTER_CRITICAL() may be dangerous with BLE because it disables temporally the interruptions, and it may cause a bug in BLE stack (I am guessing here, I am not sure).

I will try to test this hypothesis using my setup (client version).

Regards

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Thanks @RobertoHE

Add() calls to xQueueSend() and copies only 1 byte to Queue. When xQueueSend() is running, the queue is locked and protected, and that's fine. But when xQueueSend() ends, it creates some little gaps between a add() function and the next one in which Queue is unprotected, doesn't it?

True, very good catch!
I create a new Branch with a Semaphore around the receive call (doing the 1 byte at a time adding using xQueueSend) and xQueueReceive

Admitting, I'm not an ESP32 threading expert, just relying on some old knowledge of thread syncing; please check

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi @lathoub

I tried to adapt your code to my client class and it still crashed in the same way.

I captured another log. In this case, parser detected a middle timeStamp byte as SysEx start wrongly.

I used the same setup as the last test: I only added 3 types of events (noteOn, noteOff and afterTouch). Midi controller coudn't generate a SysEx in this setup.

In this case, receive() parser miss-interpreted a timestamp that had a valued of F7 or F0 and it interpreted this value as a SysEx start or end. In this case, I think that parser readed a middle-timestamp wrongly and timestamp separator had a value over 0x80 (128).

Example MSG: 08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
Decoder:
08 F7 -> TimeStamp -> Must be removed
80 -> Note Off Ch1
34 -> Note
2B -> Vel
F7 -> TimeStamp -> MUST BE IGNORED -> It is not a SysEx start
81 -> Note Off Ch2
34 -> Note
2B -> Vel
F8 -> TimeStamp -> MUST BE IGNORED
82 -> Note Off Ch3
34 -> Note
2B -> Vel

The receiver() parser might have interpreted wrongly the timeStamp separator F0 as SysEx start, and it continued processing like SysEx message until it received 128 byte (F7 SysEx not present and buffer was full).

You can see how parser missed when it returned a type: F0 (SysEx) with a lenght of 128 (max size of SysEx). And the follow parsed message did the same.

LOGS:
(remember that some messages may be represented divided by another interruption)

-New MSG-
96 F2 D2 3 F4 D0 10 F4 D1 10

-New MSG-
96 F5 D2 10 F7
### Parser ###
Type: D0
Lenght: 0
 D0 38 F7 D1 38
Channel: 2
-New MSG-
96 F8 D2 38 FA D0 69 FA D1 69


-New MSG-
96 FB D2 69 FD D0 7F FD
### Parser ###
Type: D0
 D1 7F 
Lenght: 0
Channel: 3

-New MSG-
96 FE D2 7F

### Parser ###
Type: D0
Lenght: 0
Channel: 1

### Parser ###
Type: D0
Lenght: 0
Channel: 2

### Parser ###
Type: D0
Lenght: 0
Channel: 3

### Parser ###
Type: D0
Lenght: 0
Channel: 1

### Parser ###
Type: D0
Lenght: 0
Channel: 2

### Parser ###
Type: D0
Lenght: 0
Channel: 3

### Parser ###
Type: D0
Lenght: 0
Channel: 1

### Parser ###
Type: D0
Lenght: 0
Channel: 2

### Parser ###
Type: D0
Lenght: 0
Channel: 3

-New MSG-
9E A8 80 28 0

-New MSG-
9E A9 81 28 0 AA 82 28 0

-New MSG-
9F B4 80 29 1 B4 81 29 1 B5 82 29 1

-New MSG-
9F BF D0 64 BF D1 64 C0 D2 64

-New MSG-
9F C2 D0 14 C3 D1 14 C3 D2 14

-New MSG-
9F C5 80 2B A C5 81 2B A C6 82 2B A C6 D0 0 C7 D1 0

-New MSG-
9F C7 D2 0

-New MSG-
A0 91 80 35 30 92 81 35 30 92 82 35 30

-New MSG-
A0 97 80 32 2D 98 81 32 2D

-New MSG-
A0 98 82 32 2D

-New MSG-
A0 9D 80 34 3A 9D 81 34 3A 9E 82 34 3A

-New MSG-
A0 CF 80 3C 0 D0 81 3C 0 D0 82 3C 0 D1 80 37 E

-New MSG-
A0 D1 81 37 E D2 82 37 E

-New MSG-
A0 E9 80 3B 2 EA 81 3B 2 EA 82 3B 2

-New MSG-
A1 82 80 39 1 82 81 39 1 83 82 39 1

-New MSG-
A7 FD 90 47 41 FE 91 47 41 FE 92 47 41 FF 90 45 39

-New MSG-
A7 FF 91 45 39

### Parser ###
Type: F0
Lenght: 128
Channel: 0

-New MSG-
A8 80 92 45 39

-New MSG-
A9 C8 80 47 36 C9 81 47 36 CA 82 47 36

-New MSG-
A9 CC 80 45 2E CC 81 45 2E CD 82 45 2E

-New MSG-
AE E7 90 45 D E7 91 45 D E8 92 45 D

-New MSG-
AF 8C 90 47 19

-New MSG-
AF 8C 91 47 19 8D 92 47 19

-New MSG-
B0 F4 80 47 16 F4 81 47 16 F5 82 47 16

-New MSG-
B1 88 80 45 23 89 81 45 23 89 82 45 23

-New MSG-
BA EA 90 43 1E EA 91 43 1E EB 92 43 1E

-New MSG-
BC D0 80 43 1E D0 81 43 1E D1 82 43 1E

-New MSG-
83 B9 90 41 13

-New MSG-
83 BA 91 41 13 BA 92 41 13

-New MSG-
85 B0 80 41 1A B0 81 41 1A B1 82 41 1A

-New MSG-
8A B4 90 40 18 B4 91 40 18

-New MSG-
8A B5 92 40 18

-New MSG-
8A B8 90 3E 22 B9 91 3E 22 B9 92 3E 22

-New MSG-
8C 97 80 3E 1E 97 81 3E 1E 98 82 3E 1E

### Parser ###
Type: F0
Lenght: 128
Channel: 0

-New MSG-
8C A2 80 40 1C A4 81 40 1C

-New MSG-
8C A4 82 40 1C

-New MSG-
90 AF 90 3E A

-New MSG-
90 AF 91 3E A B0 92 3E A

-New MSG-
92 DD 80 3E 16

-New MSG-
92 DD 81 3E 16 DE 82 3E 16

-New MSG-
9B 93 90 3E 12 94 91 3E 12 94 92 3E 12

-New MSG-
9B AD 90 40 19

-New MSG-
9B AD 91 40 19 AE 92 40 19

-New MSG-
A6 CB 80 40 1F

-New MSG-
A6 CC 81 40 1F CC 82 40 1F

-New MSG-
A6 E1 80 3E 25 E2 81 3E 25 E3 82 3E 25

-New MSG-
AC 80 90 3E 11 81 91 3E 11

-New MSG-
AC 81 92 3E 11

-New MSG-
AE CE 80 3E 23 CF 81 3E 23 CF 82 3E 23

-New MSG-
B1 A1 90 3E F A1 91 3E F A2 92 3E F

-New MSG-
B1 AF 90 40 F AF 91 40 F B0 92 40 F

-New MSG-
B4 9E D0 A

-New MSG-
B4 9E D1 A 9F D2 A A1 D0 30

-New MSG-
B4 A1 D1 30 A2 D2 30

-New MSG-
B4 A5 D0 5E A5 D1 5E A6 D2 5E A7 D0 7B

-New MSG-
B4 A7 D1 7B A8 D2 7B AA D0 7F

-New MSG-
B4 AA D1 7F AB D2 7F

### Parser ###
Type: F0
Lenght: 128
Channel: 0

-New MSG-
B5 C7 D0 73 C7 D1 73 C8 D2 73

-New MSG-
B5 CA D0 33 CA D1 33 CB D2 33

-New MSG-
B5 CD D0 8 CD D1 8 CE D2 8

-New MSG-
B5 D0 D0 0 D0 D1 0 D1 D2 0

-New MSG-
B6 E6 D0 3 E6 D1 3 E7 D2 3

-New MSG-
B6 E9 D0 1A E9 D1 1A EA D2 1A

-New MSG-
B6 EC D0 40 EC D1 40 ED D2 40

-New MSG-
B6 EF D0 60 EF D1 60 F0 D2 60

-New MSG-
B6 F2 D0 7F F2 D1 7F F3 D2 7F

-New MSG-
B7 DC D0 5C DC D1 5C DD D2 5C

-New MSG-
B7 DF D0 25 DF D1 25 E0 D2 25

-New MSG-
B7 E2 D0 6 E2 D1 6 E3 D2 6

-New MSG-
B7 E5 D0 0 E5 D1 0 E6 D2 0

-New MSG-
B8 EF 80 40 22 EF 81 40 22 F0 82 40 22

-New MSG-
B8 FC 80 3E 22 FD 81 3E 22

-New MSG-
B8 FD 82 3E 22

-New MSG-
83 9E 90 3E 25

-New MSG-
83 9E 91 3E 25 9F 92 3E 25

-New MSG-
86 AC 80 3E 11 AC 81 3E 11 AD 82 3E 11

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi @lathoub

I think that I have found why receive() parser some times bugs.

I think that it is relate with the line 238 and when a package contain 2 or more events and the middle-timeStamp is a F7.

while( (buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1)) )
rPtr++;
if (buffer[rPtr + 1] == 0xF7) rPtr++;

Let me to put an example:
Example MSG: 08 F7 80 34 2B F7 81 34 2B F8 82 34 2B (the same example message that last comment)
Decoder:
08 F7 -> TimeStamp -> Must be removed
80 -> Note Off Ch1
34 -> Note
2B -> Vel
F7 -> TimeStamp -> MUST BE IGNORED -> It is not a SysEx end
81 -> Note Off Ch2
34 -> Note
2B -> Vel
F8 -> TimeStamp -> MUST BE IGNORED
82 -> Note Off Ch3
34 -> Note
2B -> Vel

In the receive() algorithm, 2 first bytes that cointains timeStamp are removed.
leftPointer is set in the 3rd position of buffer 0x80 (buffer[2]).

// Decode first packet -- SHALL be "Full MIDI message"
lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status"

while() (line 236) puts the rightPointer in 5th position, 0x2B
Unfortunately, the middle-timeStamp is a 0xF7 (it may be possible if a package constens 2 o more MIDI events) and if() from line 238 moves the rightPointer one position beyond, to the 6th position.
In that point, the message must be 80, 34, 26 (noteOff event) but line 238 added one byte more, and the message is transformed to 80, 34, 26, F7.

while( (buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1)) )
rPtr++;
if (buffer[rPtr + 1] == 0xF7) rPtr++;

The size-filter interprete the event has 4 byte lenght (else line 251).
Switch() interpretes that message is a multiple 0x80 event type (noteOff multiple instead a single noteOff)
for() loop adds in the first iteration 3 bytes (80, 34, 26) and in the second one adds other 3 bytes (80, F7*, 81*)

*You can see how a noteOff can be a pitch or a velocity over 127 wrongly. Or how NoteOn or other handler may be triggered when they shouldn't.

// look at l and r pointers and decode by size.
if( rPtr - lPtr < 1 ) {
// Time code or system
mBleClass.add(lastStatus);
} else if( rPtr - lPtr < 2 ) {
mBleClass.add(lastStatus);
mBleClass.add(buffer[lPtr + 1]);
} else if( rPtr - lPtr < 3 ) {
mBleClass.add(lastStatus);
mBleClass.add(buffer[lPtr + 1]);
mBleClass.add(buffer[lPtr + 2]);
} else {
// Too much data
// If not System Common or System Real-Time, send it as running status
switch(buffer[lPtr] & 0xF0)
{
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xE0:
for (auto i = lPtr; i < rPtr; i = i + 2)
{
mBleClass.add(lastStatus);
mBleClass.add(buffer[i + 1]);
mBleClass.add(buffer[i + 2]);
}
break;
case 0xC0:
case 0xD0:
for (auto i = lPtr; i < rPtr; i = i + 1)
{
mBleClass.add(lastStatus);
mBleClass.add(buffer[i + 1]);
}
break;
case 0xF0:
mBleClass.add(buffer[lPtr]);
for (auto i = lPtr; i < rPtr; i++)
mBleClass.add(buffer[i + 1]);
break;
default:
break;
}
}

After that, leftPointer is moved to 8th position (0x34). You can see that it is the second byte of the second event in the package, it skiped 0x81 (noteOff ch2 status).

while (true) loop starts again at the begining and the value of leftPointer is evaluated.

while (true)
{
lastStatus = buffer[lPtr];
if( (buffer[lPtr] < 0x80))
return; // Status message not present, bail

Now, leftPointer in 8th position (0x34) has a value lesser than 0x80, and it triggers the return prematurely. The 3rd event of the package is skiped completely.

For this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B), receive() had to add to the buffer the follow bytes:
(80 34 2B / 81 34 2B / 82 34 2B).
Instead, It added:
(80 34 2B / 80 F7 81)

When MIDI.read() will try to interprete this messenge... it will do some strange.

https://github.com/TheKikGen/midiXparser -> This midi parser works byte byte and I haven't see the same bug for the same input if you removed the first 2 bytes. It may help you how to the parser must discriminate correctly a SysEx and don't add a timeStamp F7 to buffer.

By the way, the semaphore protection may still necessary.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Great digging @RobertoHE
I pulled parts of the code in MSVC and was able to get something fairly stable, just be removing line 238

So remove:

if (buffer[rPtr + 1] == 0xF7) rPtr++;

Somehow there was never full support for SysEx across multiple packets, i'll look into that right now.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

By the way, the semaphore protection may still necessary.

Reading up on xQueueSend and xQueueReceive (and how they are harvested), i'm hopefull that we can do without (Branch deleted)

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

For this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B), receive() had to add to the buffer the

The first byte (0x08) is not valid, bit 7 needs to be 1 (I guess a typo) (the message from the logs had the bit set to 1)

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Great digging @RobertoHE
I pulled parts of the code in MSVC and was able to get something fairly stable, just be removing line 238

So remove:

if (buffer[rPtr + 1] == 0xF7) rPtr++;

Somehow there was never full support for SysEx across multiple packets, i'll look into that right now.

I have commented this line and I couldn't replicate the bug. This is good.

I don't know how this can affect to a SysEx message splitted in any package.

If you need some help I would investigate or try code something.

Please, review my pull request of Client #30. Don't accept it now, it needs 2 corrections (uxQueueSpacesAvailable() is useless) but I will appreciate your feedback so much.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

If you need some help I would investigate or try code something.

Yes, will let you know :-)

Please, review my pull request of Client #30. Don't accept it now, it needs 2 corrections

Wilco

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

@RobertoHE

I fixed the initial bug and initial support for (MultiPart) SysEx
https://github.com/lathoub/Arduino-BLE-MIDI/tree/MultiPartSysEx

Can you test?

I used these test sets:

    byte sysEx1Packet[] = { 0xB0, 0xF4, 0xF0, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xF4, 0xF7 };
   receive(sysEx1Packet, sizeof(sysEx1Packet));

    std::cout << std::endl << "SysEx part 1" << std::endl;

    byte sysExPart1[] = { 0xB0, 0xF4, 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
    receive(sysExPart1, sizeof(sysExPart1));

    std::cout << std::endl << "SysEx part 2" << std::endl;

    byte sysExPart2[] = { 0xB0, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xF4, 0xF7 };
    receive(sysExPart2, sizeof(sysExPart2));

    std::cout << "ble Packet with 1 MIDI messages" << std::endl;

    byte blePacketWithOneMIDIMessage[] = { 0xA8, 0xC0, 0x90, 0x3E, 0x3E };
    receive(blePacketWithOneMIDIMessage, sizeof(blePacketWithOneMIDIMessage));

    std::cout << std::endl << "ble Packet with 2 MIDI messages" << std::endl;

    byte blePacketWithTwoMIDIMessage[] = { 0xA8, 0xC0, 0x90, 0x3E, 0x3E, 0xC1, 0x91, 0x3E, 0x3E };
    receive(blePacketWithTwoMIDIMessage, sizeof(blePacketWithTwoMIDIMessage));

    std::cout << std::endl << "2 MIDI messages with running status" << std::endl;

    byte twoMIDIMessageWithRunningStatus[] = { 0xA9, 0xAE, 0xD1, 0x74, 0xAF, 0xD2, 0x74, 0xB1 };
    receive(twoMIDIMessageWithRunningStatus, sizeof(twoMIDIMessageWithRunningStatus));

    std::cout << std::endl << "2 MIDI messages with running status" << std::endl;

    byte multipleMIDIMessagesMixedType[] = { 0x00 };
    receive(multipleMIDIMessagesMixedType, sizeof(multipleMIDIMessagesMixedType));

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi @lathoub

Can you test?

I will try to test it this evening. I may add some 0xF0 to 0xFF events to complete the test, if I have time.

I fixed the initial bug and initial support for (MultiPart) SysEx
https://github.com/lathoub/Arduino-BLE-MIDI/tree/MultiPartSysEx

I have some doubts:
lPtr starts to evaluate the buffer (timeStampHigh) at the second byte (buffer[1]) instead at the beginning (buffer[0])
line 233 must be after 237 line and line 239 must be after line 241.

void receive(byte *buffer, size_t length)
{
// Pointers used to search through payload.
byte lPtr = 0;
byte rPtr = 0;
// lastStatus used to capture runningStatus
byte lastStatus;
lPtr++;
byte headerByte = buffer[lPtr];
auto signatureIs1 = CHECK_BIT(headerByte, 7 - 1); // must be 1
auto reservedIs0 = !CHECK_BIT(headerByte, 6 - 1); // must be 0
auto timestampHigh = 0x3f & headerByte;
lPtr++;
byte timestampByte = buffer[lPtr];
uint16_t timestamp = 0;

rPtr increases twice. Line 345 is unnecessary. In this part of the code, it evaluates the timeStampLow as event divider, which has only 1 byte lenght, not 2. If it increases twice, I will evaluate the next midiStatus instead timeStampLow divider.

Lines 353-356 may be redundant only if rPrt increase once. It may be considered like redundant protection, it is not bad.

rPtr++;
if (rPtr >= length)
return; // end of packet
rPtr++;
timestampByte = buffer[rPtr];
signatureIs1 = CHECK_BIT(timestampByte, 7 - 1);
if (signatureIs1)
{
timestamp = setMidiTimestamp(headerByte, timestampByte);
}
// Point to next status
lPtr = rPtr;
if (lPtr >= length)
return; //end of packet

Lines 321 to 332 are duplicated inside and afer switch(), the code does the same twice. rPtr avances twice, the first one inside of switch() and the second one after switch(). It may evaluate the next midiStatus instead timeStamp, if it increase twice.

case MIDI_NAMESPACE::MidiType::SystemExclusive:
mBleClass.add(buffer[lPtr]);
for (byte i = lPtr; i < rPtr; i++)
mBleClass.add(buffer[i + 1]);
rPtr++;
if (rPtr >= length)
return; // end of packet
rPtr++;
timestampByte = buffer[rPtr];
if (CHECK_BIT(timestampByte, 7 - 1))
{
timestamp = setMidiTimestamp(headerByte, timestampByte);
}
rPtr++;
break;

What do you opine about these corrections?

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

@RobertoHE very well spotted Sir! (I was trying to prematurely anticipating the removing of the timestamp statements)
Correction on its way

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi again

Are you sure that these lines are necessary?

Lines 321 to 332 are duplicated inside and afer switch(), the code does the same twice. rPtr avances twice, the first one inside of switch() and the second one after switch(). It may evaluate the next midiStatus instead timeStamp, if it increase twice.

case MIDI_NAMESPACE::MidiType::SystemExclusive:
mBleClass.add(buffer[lPtr]);
for (byte i = lPtr; i < rPtr; i++)
mBleClass.add(buffer[i + 1]);
rPtr++;
if (rPtr >= length)
return; // end of packet
rPtr++;
timestampByte = buffer[rPtr];
if (CHECK_BIT(timestampByte, 7 - 1))
{
timestamp = setMidiTimestamp(headerByte, timestampByte);
}
rPtr++;
break;

I don't know if after a splitted SysEx message can appear other event in the same package. In that case, it moves rPrt twice wrongly beyond midiStatus of the next event.

I let me put an example (I don't know if it possible):

Package 1: 80 F7 80 34 2B F7 81 34 2B F8 **F0** 00 11
Package 2: 91 A2 22 33 44 55 **F7** A3 90 34 90 A4 80 34 00

PCK1:
80 F7 -> TimeStamp High and Low
80 -> NoteOff Ch 1
34 -> pitch
2B -> Velocity
F7 -> TimeStamp Low (divider)
81 -> NoteOff ch2
34 -> pitch
2B -> Velocity
F8 -> TimeStamp Low (divider)
F0 -> SysEx Start
11 22 -> SysEx Data

PCK2:
91 A2 -> TimeStamp High and Low
33 44 55 -> SysEx Data
F7 -> SysEx End
A3 -> TimeStamp Low (divider)
90 -> noteOn Ch 0
34 -> pitch
90 -> velocity
A4 ->TimeStamp Low (divider)
80 -> noteOff Ch 0
34 -> pitch
90 -> velocity

PCK1 set the flag sysExContinuation because it conains F0 but not F7.

PCK2 is processed correctly until F7 as SysEx. switch() go to case MIDI_NAMESPACE::MidiType::SystemExclusive: and for() add the rest of message to buffer. Until now, everything is correct. rPtr points to F7 (buffer[6])

Inside case MIDI_NAMESPACE::MidiType::SystemExclusive:, rPtr is increased (buffer[7] = A3 TimeStampLow) by lines 319-321 and parser checks that package is completely scanned. In the example, that is false, and parse continue.

In lines 323-327, it captures the timeStampLow (buffer[7] = A3) and after it increases rPtr to (buffer[8] -> midiStatus 90 noteOn).

In line 329, rPtr is moved again to buffer[9](why?) (pitch of noteOn 34). In this point, it have skipped the midiStatus. After that, switch() gets exit and parser continue at line 336.

Lines 337 to 346 are the same that lines 319 to 327 and parser does twice the same for a SysEx.

At the line 337, rPtr is increased again and now it points to buffer[10] (vel of noteOn). It is not the end of the message, and the parser continue to line 341.
At the line 341, parser capture buffer[10] (vel of noteOn) like timeStampLow and move the point to buffer[11] (timeStampLow).
After that. while() loop ends and the parser starts again with lPtr in buffer[11] (timeStampLow A4) and it miss-interpretes it like a midiStatus. .... I don't continue explaining, but you can suppose what happen after that....

In resume, it miss-interpreted everything after F7 SysEx end (NoteOn and NoteOff events).

case MIDI_NAMESPACE::MidiType::SystemExclusive:
mBleClass.add(buffer[lPtr]);
for (byte i = lPtr; i < rPtr; i++)
mBleClass.add(buffer[i + 1]);
rPtr++;
if (rPtr >= length)
return; // end of packet
timestampByte = buffer[rPtr++];
if (CHECK_BIT(timestampByte, 7 - 1))
{
timestamp = setMidiTimestamp(headerByte, timestampByte);
}
rPtr++;
break;
default:
break;
}
}
rPtr++;
if (rPtr >= length)
return; // end of packet
timestampByte = buffer[rPtr++];
signatureIs1 = CHECK_BIT(timestampByte, 7 - 1);
if (signatureIs1)
{
timestamp = setMidiTimestamp(headerByte, timestampByte);
}
// Point to next status
lPtr = rPtr;
if (lPtr >= length)
return; //end of packet
}

If you repeat the sequence with the 2 packages and you remove the lines 319-329, you will can see that message will be processed correctly.

Would you check it, please?

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

No sure if PCK1 and PCK2 are valid according to the spec - i need to further check.

Here is what the spec says for (multipacket) SysEx:
Capture

In case of a spanning SysEx over PCK1 and PCK2, PCK2 should start with a TimestampHigh and no low (so only 1 byte). In the PCK2 example, it contains 2 bytes.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

I fixed an additional bug (test for SysExEnd) and added MSVC parser test code

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Further cleanup and testing (in MSVC, not in the Arduino) and cleared all of the test data - getting closer :-)
(Including fixing/cleaning some of the remarks made above)

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Please, check #32.

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Changes accepted - thank you @RobertoHE

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

* It recommend not use runningStatus by default. Only use if parser accepts runningStatus and your application has a so high transmission rate.
*/
//#define RUNNING_ENABLE

You recommend not using RUNNING_ENABLE - yet the underlying parser does support runningStatus for incoming messages and runs fine with the RUNNING_ENABLE enabled or disabled (running msvc examples renders the same result). I kinda like the efficiency that runningStatus brings.

Based on the current examples in BLEMIDI_Sim.h, i recommend making RUNNING_ENABLE permanent. WDYT @RobertoHE ?

byte sysExAndRealTime[] = { 0xB0, 0xF4, // header + timestamp
0xF0, // start SysEx
0x01, 0x02, 0x03, 0x04, // SysEx data
// RealTime message in the middle of a SysEx
0xF3, // timestampLow

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

if (sysExContinuation)
midiType = SystemExclusive;

Is this code needed? Looks like it can be removed. The switch below does not test for SystemExclusive

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Parser is going OK on an ESP32 Devkit v1 using the default ESP32 BLE stack - I tried all kinds on MIDI messages and all come through - good stuff. I'm good to commit to master!

FYI: I also tried the NimBLE stack and also ran OK, but I noticed that when Disconnecting, the connect button on the Apple MIDI setup does not come back and the device disappears.

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

FYI: I also tried the NimBLE stack and it also ran OK, but I noticed that when Disconnecting, the connect button on the Apple MIDI setup does not come back and the device disappears.

I have never used Apple MIDI. I don't know anything about it. What is the problem? What is the setup?

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

I have never used Apple MIDI. I don't know anything about it. What is the problem? What is the setup?

oho - you see, i can't do 2 things at the same time: work on AppleMIDI and BLE-MIDI :-).

What i was trying to say: the ESP32 connects fine to the Apple BLE stack, but after disconnect, the ESP32 does not re-appear and i can no longer connect. Does it behave correctly with you?

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

Hi again @lathoub

I have seen that in this repo there is a common open issue: the server doesn't advertise (not only for apple midi).

When I was coding the client class, I used as a template the nimble server of this repo and the client example from nimble repo. I noticed that nimble example allowed multi-client connection and it advertised continuously inside loop. Your nimble server only advertise in setup and it doesn't do anything after a disconnection (the server doesn't advertise itself after a disconnection). I am not sure that nimble has a method which advertices continously.

Client mode doesn't use advertiments, it scans the advertised devices and it connects to them, but you can use that mechanism as an example.

Client mode has an "auto-reconnect mechanism" inside read (available) function that is managed with onConnect and onDisconnect callbacks and a global variable. For a server, a sofisticate mechanism for advertisement is not needed, but it may need to advertise periodically when it accepts connections from clients (after disconnection, for example). You must decide whether the server accepts one or more clients, because it determines the strategy of performing advertisements. If the server only supports one client (which I think is preferable), you can use a similar mechanism, such as client class, changing scan functions for advertising.

I can't code anything until September.

from arduino-ble-midi.

RobertoHE avatar RobertoHE commented on September 18, 2024

What i was trying to say: the ESP32 connects fine to the Apple BLE stack, but after disconnect, the ESP32 does not re-appear and i can no longer connect. Does it behave correctly with you?

Hi @lathoub . Check this function:
https://h2zero.github.io/esp-nimble-cpp/class_nim_b_l_e_server.html#a6bfd923ecd0ea06d5564343ab7209122

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

Hi @lathoub . Check this function:
https://h2zero.github.io/esp-nimble-cpp/class_nim_b_l_e_server.html#a6bfd923ecd0ea06d5564343ab7209122

Fixed!

from arduino-ble-midi.

lathoub avatar lathoub commented on September 18, 2024

All code moved to the master branch. Thank @RobertoHE
(I activated the RUNNING_ENABLE flag, as the underlying parser uses running status)

from arduino-ble-midi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.