midilab / uClock

A tight BPM clock generator for Arduino and PlatformIO using hardware timer interruption. AVR, Teensy, STM32xx, ESP32 and RP2040 support
https://midilab.co/umodular
MIT License
153 stars 19 forks source link

Support for the ESP32/ESP32S2/ESP32S3 platforms #18

Closed copych closed 1 year ago

copych commented 1 year ago

These MCUs are powerfull enough to run MIDI stuff along with some audio routines, also S2 and S3 are capable of being USB-HOST devices, so stand-alone units allowing direct connecting of USB-MIDI keyboards/controllers can be built.

copych commented 1 year ago

@midilab @m-r-m-s @eefh Is there a chance?

midilab commented 1 year ago

yep, i've take a brief look so far on other implementations, but didn't had a time to work on it, looks doable and possible.

for sure its a huge gain for lowcost high power cpu music/clock machines!

copych commented 1 year ago

great!

midilab commented 1 year ago

Implemented at this branch: esp32-support

Please, if you could test it for me: ESP32UsbMasterMidiClock

I also i see a lot of different stacks to implmement usb midi on esp32... so i didn't code it yet on the esp32 example above.

So since you're doing for longer than me esp32's stuffs, maybe you can complete the example with a working USB midi and then pull request it back so we can close this issue and merge with master branch for release.

If you run the example you'll see the blue led blinking at 126bpm time, it means it works.

copych commented 1 year ago

Hello, I'm very glad to know that you're dealing with esp32 now! Actually, AFAIK https://github.com/FortySevenEffects/arduino_midi_library is a standard de-facto. As I see, this is what you used in the example. I haven't test this library along with the USB-HOST, cause this topic is still unclear for the esp32-s2 and -s3, and probably they haven't yet ported this ability for Arduino, but otherways it suits almost any needs. I will try your example ASAP.

copych commented 1 year ago

https://gist.github.com/copych/615f9c046fb44945882935ee8147ca88

midilab commented 1 year ago

you need to install uclock library using the branch i've linked for you.

$ git clone https://github.com/midilab/uClock.git $ cd uClock/ $ git checkout esp32-support

Then copy the uClock directory to your arduino libraries install folder

copych commented 1 year ago

I've managed to run your code on both ESP32 and ESP32-S2! It blinks as supposed, one long - three shorts. In different boards definitions LED_BUILTIN defined differently, and in some cases it's absent, while in some cases it's present, but has an inverted logic, blinking in the LOW state... BPM is 126 as I tapped along.

midilab commented 1 year ago

great! thanks for testing on your side!

can you name the models and details about it?(model/LED pin/inverted?) so then i can code some flags to handle accordly.

Anyway good to know, i will proceed porting the same timer stuffs to uCtrl now, then we can try to run the aciduino v2 on it.

midilab commented 1 year ago

Well i just realize we got a problem with ISR calls on esp32 and the way uCtrl and uClock works, since its a multicore i need to investigate a little bit to know what is going on, as an example, if you try to uncomment the Serial.print(MIDI_CLOCK) line then it freezes.. Example

How are you dealing with Serial for you MIDI stuff on esp32?

By the way i port the timer for uCtrl too but still need to figureout how to make it fully compilant with esp32 multicore, i got a experimental version of aciduino v2 running on a esp32 and since its using uCtrl that controls the interrupt acess looks like its running fine.

i've push the experimental code to a new branch called esp32-experiental-support if you wnat to check out too, try to run aciduino v2 and tell me about it please!

you can check the wiring here

please be carefull with pins numbering(you can see more detailied open with fritzing software) since i see that some wroom boards have sligth different pinouts.

git clone https://github.com/midilab/aciduino.git cd aciduino git checkout esp32-experiental-support git submodule update --init --recursive

Then you can load the Aciduinov2 project on arduino IDE.

Its running without freezing aparenttly, but looks like we got the inverted problem for buttons on HIGH and LOW, did you got a list of those esp32 you tested for inverted or non inverted? that needs to be coded for fully support to be ready

copych commented 1 year ago

I'm away from the pc at the moment, but concerning the esp32 multicore, in the Arduino environment you're actually dealing with just one core, and it's enough in most cases. You only should pay attention to the variables and methods involved in the ISR routines, they should be defined with special modifiers like IRAM_ATTR to be placed in the fast RAM without transferring from flash/caching. And the methods should be really fast, just setting some vars and passing control quickly, otherwise you'll be getting "guru meditation errors". I'll look at the code, but probably only tomorrow in the evening.

copych commented 1 year ago

about the "button inverted hi and lo", it depends on the build, you can choose yourself, if the button shall connect to 3V3 or to GND, and be INPUT_PULLUP or INPUT_PULLDOWN. You also should check the pinout to be sure that choosen pins support the mode requested together with the functions turned on (wifi, adc and sd sometimes "eat" pins).

copych commented 1 year ago

About serial ports. Firstly, to avoid future confusing, ESP32, ESP32-S2, ESP32-C3 and ESP32-S3 are totally different MCUs, inspite of the common ESP32 prefix. Now I'm talking about ESP32. Here you can find detailed info on the topic: https://microcontrollerslab.com/esp32-uart-communication-pins-example/ , and the main subject is that here we have 3 UARTS: one is most cases connected to DevKit board's USB, while the two others you can use as you wish, but again with some precautions ).

Serial.begin()   //if using UART0
HardwareSerial SerialPort(1)  //if using UART1
HardwareSerial SerialPort(2)  //if using UART2

there're also default instances, as far as I remember, Serial1 and Serial2 for UART1 and UART2. but I am not 100% sure of naming. UART0 native pins are GPIO3 and GPIO1 (bound to USB) UART1 default pins are GPIO9 and GPIO10, but these pins are always used by internal FLASH, so reassignment required. UART2 default pins are GPIO16 and GPIO17, but these pins are used by PSRAM, so you'd better reassign them too.

So, say, you want Hairless MIDI on the USB port, in modern way with templates it'd look like this:

struct CustomBaudRateSettings : public MIDI_NAMESPACE::DefaultSerialSettings {
  static const long BaudRate = 115200;
};
MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings> serialMIDI(Serial);
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>> MIDI((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>&)serialMIDI); 

or, if you just want ordinary MIDI port at 31250 bod, you can (this is an old-fashioned code, but easier to read, I just copy-paste):

struct Serial2MIDISettings : public midi::DefaultSettings{
  static const long BaudRate = 31250;
  static const int8_t RxPin  = 5;
  static const int8_t TxPin  = 18; 
};
HardwareSerial MIDISerial(2);
MIDI_CREATE_CUSTOM_INSTANCE( HardwareSerial, MIDISerial, MIDI, Serial2MIDISettings );
midilab commented 1 year ago

thanks for the explanation, i will soon review the code, i got aciduino v2 running on esp32 wroom(i got one for my self), but the LOW HIGH is swapped so it goes crazy like clicking everywhere. i will try to make a option at code level to change that behaviour based on the model. Soon i'll post some news!

copych commented 1 year ago

If the wiring diagram is the one you mentioned earlier, then to eliminate this crazy button behaviour you should use pinMode(pin, INPUT_PULLUP) for each button. Also, there's a table of GPIOs with explanations which ones you should or shouldn't use in what cases: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/

midilab commented 1 year ago

uCtrl is already doing pullup as default setup for buttons. i just realize it needs a deep investigation of the esp32 multicore arch to fully port uCtrl and uClock to work on aciduino.

I find out that the freezes happen on serial outputing at ISR call for clock and notes. I've create a working example of clock for ESP32 without using serial output at ISR, but the whole uCtrl and uClock apps sequencers work in a diffrent way.

So, i'll try to read a bit more about the esp32 arch and find a way out for this port, if you get your aciduino running at your esp32 now you see random freezes but works for a while...

copych commented 1 year ago

I'll try to check it tonight

copych commented 1 year ago

hello @midilab , I've managed to compile the code , but it keeps on resetting itself, there's no error, but reset. I haven't investigated what's causing these resets, and i also haven't looked thru the code. As i see some portions of unreadable symbols in the port monitor, I can presume that the app is trying to use the usb port for midi, while it is also used for debugging (wild guess).

midilab commented 1 year ago

I just take a brief read at some docs about esp32 arduino environment to understand a bit more about the archtecture.

Since its a multicore arch, and for arduino it runs FreeRTOS, the correct way of porting is not using timers as by the others platforms(avr, teensy, xiao...), but instead make use of realtime tasks, and instead of locking the execution of interrupts to avoid non atomic operations we should use mutexes, i guess its not that hard to write a port like that, i will try it soon

Currently, it wont run without freezing because of that multicore and timer usage being different from others platforms. specially the usage of Serial under ISR on esp32(wich goes fine with interruption control over atomic operations on the other platforms ported so far)

Thanks for testing and reporting back, i will continue with the port using FreeRTOS portion of esp32 arduino port, by using realtime tasks and mutexes. So i will soon release another codebase for tests.

copych commented 1 year ago

btw, I've updated AcidBox, so now it can be compiled without PSRAM, but in exchange, reverb and delay to be sacrificed, and only 65k for drum samples. So if you have any esp32, you can try. Quality audio out still requires external i2s DAC.

midilab commented 1 year ago

looks great! is there a way to use internal DAC too? even if it doesn't sounding that good?

copych commented 1 year ago

yes, define USE_INTERNAL_DAC will do the thing, but it requires scaling adjusting, cause I set it wrong. PS I forgot about the esp32 core versions. Internal DAC will only work with 2.0.0 and earlier. After that they killed the feature and six minor versions passed, but they've still not fixed it {{{

midilab commented 1 year ago

nice! i will try to find a time to play with my new esp32 wroom edition, i was planing to get into DSP on those arm for years now, so its a good start point with 303sh basses from acidbox project.

copych commented 1 year ago

It seems that I've fixed i2s output to the internal DAC. It now works with core 2.0.6. Sounds like 8 bit )

midilab commented 1 year ago

i think its a great option to have, since most people dont have a I2S DAC. Also 8bits sound is fancy! specially for drums, kinda gain some low punch when goes lower bitrate.

midilab commented 1 year ago

So, great news, i made some changes to make it running without reseting.

You can test it on the main branch(no release yet so you need to init submodules too)

$ git clone https://github.com/midilab/aciduino.git $ cd aciduino/ $ git submodule update --init --recursive

The only problem i can't still figureout is a reset problem when reading midi input for external sync, maybe the only way for external sync will be to add a realtime task for that.

But anyway, looks like the main sequencer is ok and also the midi output via Serial, i didn't hook up on any midi interface 3.3v because i dont have one over here now.

Let me know!

copych commented 1 year ago

Oh! Really great! Tell me please, what pin should I use to get midi out of your sequencer? Or is there some kind of setup? Or just point where midi starts? I'll try the app soon.

midilab commented 1 year ago

The code is currently using at 1_uctrl.ino: MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI1);

The pinage for interface: https://github.com/midilab/aciduino/blob/master/v2/hardware/imgs/aciduino_v2-esp32_bb.png

copych commented 1 year ago

Happy new year! I still got reboots: Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1). I believe I downloaded everything from your repo's correct branches though The only thing I've changed is Serial to Serial2 in MIDI_CREATE_INSTANCE

midilab commented 1 year ago

the correct branch is the main branch. just in case i've pack the release here you can just download and use it: https://github.com/midilab/aciduino/releases/download/v2.0.0-beta9/aciduino-v2b09.zip

can you test with Serial instead to check if reboots? can you confirm are you using the main and last commit?

Also, have you change the Core 1 and Core 0 definitions on IDE? for arduino you can choose one for events and other for arudino, the default is Core 1 for both.

happy new year!

copych commented 1 year ago

It seems to work. About "crazy buttons": GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal pull-up or pull-down resistors. GPIOs 5, 18, 19 and 23 should be safe to use in exchange, or we'll have to use external resistors. Also it reboots sometimes, but I wasn't monitoring. For example, if i contoniously press shift+down, on the third time it shows some splash info page and reboots. And shift pressed sometimes isn't detected if I hold it and press other buttons in series. Also I may got it wrong, but are drum patterns generated by instrument, not the whole thing? Or is it just wrong mapping so i only hear BD?

midilab commented 1 year ago

It seems to work. About "crazy buttons": GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal pull-up or pull-down resistors. GPIOs 5, 18, 19 and 23 should be safe to use in exchange, or we'll have to use external resistors. Also it reboots sometimes, but I wasn't monitoring. For example, if i contoniously press shift+down, on the third time it shows some splash info page and reboots. And shift pressed sometimes isn't detected if I hold it and press other buttons in series.

Uhmmm thanks for the info! all those pins can be setup o 0_hardware_setup.ino inside ESP32 code flag guards. About the freezes i think i have a idea about what could be happening, i will soon commit the changes so you can check.

What are the configuration for Core 1 and Core 0 Arduino/Events you're using for that build? What Serial number did you use for that build?

Also I may got it wrong, but are drum patterns generated by instrument, not the whole thing? Or is it just wrong mapping so i only hear BD?

There is a "note" param for each drum part, i've setup the default midi definitions for drums(most drum devices use the same layout), but there are cases where your drum device could be waiting a different midi note for a part, try to change this "note" param on seqr page, change the drum part using the knob when step sequencer is selected.

Thanks for the testings so far.

copych commented 1 year ago

I used standard settings for cores (i can't check right now), and Serial2 on GPIO 16 and 17. Midi output (17) then goes to midi in of another module, where I run 303+808 emu. And it works as supposed. I have changed GPIOs in hw_config, otherwise I couldn't use it. Drum sampler in my current setup has every midi note [0..127] assigned repeating by octaves.

midilab commented 1 year ago

Can you share the changes that worked for you for hardware pins? the part of the file with definitions

Did you check if the drum part is muted? the mute pattern grid will mute or unmute parts depending on how you setup the parts on the grid.

copych commented 1 year ago

I am not at the PC, but changes were exactly as mentioned, I changed 34..37 to 5, 18, 19, 23. It may be not optimal, as I saw later, cause you reserved these pins for a pot or for an encoder. So we can use some other variants from the [12..33] range. Surely, 16, 17, 21, 22 are busy already. We can also use 4 and 0 (some limitations on boot). BTW, 34..39 ideally suit for pots connecting, cause no need for pulling up/down, so we can reassign pots to them. Some encoders also has built-in pulling resistors. Another thing I forgot to mention is UI confirmation for some actions, which doesn't change the state. For example , pressing "generate" button has no impact on the UI. It'd be much better if the engaged function shortly blinked, visually confirming the action performed.

copych commented 1 year ago

About the drum parts I shall dbl check

midilab commented 1 year ago

since the uClock is running along with ESP32 i will close this issue, any ideas about the aciduino development or bugs please report on the aciduino project.