Teensy communicatie


  1. MIDI
    1. USB-mode
    2. Verzend midi
    3. Data filteren
    4. Datastroom verkleinen
    5. Ontvang midi
  2. Midi in Max
    1. Midi ontvangen
    2. Midi versturen
  3. Serial
    1. Data versturen naar Max
    2. Data ontvangen in Max

Je kan op meerdere manieren data doorsturen vanaf je Teensy naar een ander programma. Hieronder lichten we er twee toe, MIDI en serial.

MIDI

Jouw Teensy kan in een paar simpele stappen een midi-device worden waarmee je data kan versturen naar Max, SuperCollider of een DAW. Daarnaast kan je ook data ontvangen via MIDI uit een van deze programma's. Het voordeel van MIDI is dat het snel werkt en dat je het makkelijk op veel manieren kan gebruiken. Het nadeel is dat je beperkt bent in de resolutie die je door kan sturen. MIDI werkt standaard alleen met getallen tussen de 0 en 127. Als je dus waarde uit een analoge sensor wil doorsturen, dan reduceer je het bereik van 0 - 1023 wat je daar standaard mee hebt tot 0 - 127. Iets dat bij bijvoorbeeld cutoff-frequentie van een filter hoorbaar kan zijn.

Een gedetailleerde uitleg van USB MIDI op Teensy's vind je hier

USB-mode

Zorg dat je de onder Tools->USB Mode de modus Serial + MIDI selecteert. Als je nu je code opnieuw compileert zal de Arduino IDE je Teensy zich laten gedragen als een MIDI device en ook de mogelijkheid voor Seriële communicatie open laten. Het is belangrijk dat je deze modus selecteert voordat je de code waarin je usbMIDI (zie hieronder) gebruikt verifieert. Als je deze modus niet selecteert, kan je ook deze code niet gebruiken.

Verzend midi

Nu kan je gebruik maken van de volgende functies om MIDI te sturen naar je computer:

    usbMIDI.sendNoteOn(note, velocity, channel);
    usbMIDI.sendNoteOff(note, velocity, channel);
    usbMIDI.sendControlChange(control, value, channel);

Voor meer functies zie hier Let op! Zorg ervoor dat je niet met volledige kloksnelheid van de Teensy data doorstuurt, dan ontvangt de computer veel te veel data, waardoor het lastig wordt deze te verwerken. Maak dus gebruik van een delay(), ellapsedMillis of millis() om er voor te zorgen dat de verstuursnelheid wat lager ligt.

Als je de data wil filteren, kan je informatie uit de volgende link halen

Data filteren

Data die op een Teensy een analoge poort binnenkomt, kan erg ruizig zijn. Dit houdt in dat, als je het uit zou lezen in de Serial Plotter er geen rechte lijn binnenkomt, maar dat er pieken en dalen inzitten. Als je deze spikes niet wegfiltert en vervolgens doorstuurt, dan krijg je chaotische data in het programma waar je de MIDI wilt gebruiken. Deze spikes kan je wegfilteren met een simpel LowPass filter. Dit kan je eventueel hardwarematig aanpakken, met een weerstand en een condensator. Zie hiervoor deze link.

Digitaal Low Pass filter

Handiger voor nu is het om het digitaal aan te pakken: De simpelste manier is om elke keer dat je de waarde uitleest van de potmeter, deze te vermenigvuldigen met (bijvoorbeeld) 0.3 en dat resultaat op te tellen bij 0.7 x het vorige resultaat. Hiermee krijg je een steeds aanpassend gemiddelde van de potmeterwaarde, waarmee de pieken eruit zijn.

In code ziet dat er als volgt uit

//variabele om het gemiddelde in op te slaan
int potavg = 0;

void setup() {
    pinMode(A0,INPUT);
    Serial.begin(9600);
}

void loop() {
    int pot = analogRead(A0);
    potavg = (pot * 0.3) + (potavg * 0.7);
    usbMIDI.sendControlChange(1, potavg/8, 1);
    delay(1);
}

Datastroom verkleinen

Nu de data gefilterd is, zal je merken dat er nog steeds een grote hoeveelheid data doorgestuurd wordt. Als je je zelfgebouwde MIDI-controller wil koppelen aan bijvoorbeeld de MIDI-learn-functie van je facoriete DAW, zal je merken dat dit niet handig is, omdat (bij meerdere potmeters) elke potmeter altijd waardes doorstuurt, waardoor alleen de laatste wordt opgepikt. Om dit te voorkomen moet je zorgen dat waardes alleen worden doorgestuurd als deze veranderen. Dit doe je door bij te houden wat de vorige waarde was en of deze gelijk is aan de huidige waarde of niet. Daarbij kan het gebeuren dat, ondanks het filteren, de waarde nog wel eens 1 of 2 verschilt. Dus het is nog handiger om het verschil tussen oud- en nieuw te bekijken en alleen door te sturen als het verschil groter is dan bijv 2. In code ziet dit er als volgt uit (uitgebreid op de code voor het filteren)

//variabele om het gemiddelde in op te slaan
int potavg = 0;
int prevPot = 0;

void setup() {
    pinMode(A0,INPUT);
    Serial.begin(9600);
}

void loop() {
    int pot = analogRead(A0);
    potavg = (pot * 0.3) + (potavg * 0.7);
    if (abs(prevPot - potavg) > 2) {
        usbMIDI.sendControlChange(1, potavg/8, 1);
        prevPot = potavg;
    }
    delay(1);
}

Ontvang midi

Wil je MIDI ontvangen op je Teensy, dan kan je nu gebruik maken van callback functies. Dat zijn functies die worden uitgevoerd als er iets specifieks gebeurt. Dit kan bijvoorbeeld zijn dat je een functie uitvoert wanneer er een MIDI noot wordt ontvangen op je Teensy.

    usbMIDI.setHandleNoteOn(usbMIDIsetHandleNoteOn);
    usbMIDI.setHandleNoteOff(usbMIDIsetHandleNoteOff);

In dit geval behoor je zelf een void myNoteOn() { ... } functie te maken. Om dit te laten werken moet je ook nog iedere loop() de functie usbMIDI.read() aanroepen. Dit vertelt je Teensy dat deze moet kijken of er geen nieuwe MIDI berichten binnen zijn gekomen.

Voorbeeld:

void setup() {
    usbMIDI.setHandleNoteOn(usbMIDIsetHandleNoteOn);
    usbMIDI.setHandleNoteOff(usbMIDIsetHandleNoteOff);
    usbMIDI.setHandleControlChange(usbMIDIsetControlChange);
}

void loop() {
    usbMIDI.read();
}

void usbMIDIsetHandleNoteOn(byte channel, byte note, byte velocity) {
    if (channel == 1) { //lees berichten via channel 1
        playNote(note, velocity);   
    }
}

void usbMIDIsetHandleNoteOff(byte channel, byte note, byte velocity) {
    if (channel == 1) {
        stopNote(note, velocity);   
    }
}

void usbMIDIsetControlChange(byte channel, byte controller, byte value) { 
    Serial.println("Received MIDI CC on controller " + String(controller) + " with value " + String(value));   
}

⬆️

Midi in Max

Je kan Midi het makkelijkst versturen vanuit of ontvangen in Max. Hieronder staat hoe je dat het best kan doen.

Midi ontvangen in Max

Voor het ontvangen van midi in Max kan je het ctlin-object gebruiken. Zie hieronder voor een afbeelding.

Screenshot%202025-04-03%20at%2012.55.48

Let op als je in Max for Live werkt moet je zorgen dat je de data überhaupt binnen kan krijgen in Live. Dit doe je door in de Live-settings onder het kopje link, tempo & midi het vinkje bij remote aan te vinken bij de Teensy Midi.

Midi sturen vanuit Max

Om MIDI te sturen vanuit Max kan je waarschijnlijk het beste MIDI CC berichten gebruiken. Die stuur je makkelijk met de volgende combinatie van objecten: maxTeensyMIDI

Dubbelklik even op ctlout om je Teensy te selecteren als MIDI Device


⬆️

Serial

Met seriële communicatie stuur je byte voor byte data door van de Teensy naar de computer of van de computer naar de Teensy. MIDI is een speciale vorm van seriële communicatie waarbij er een aantal afspraken zijn gemaakt over wat welke byte of volgorde van byte betekent. Je kan echter met de Teensy ook zelf je eigen afspraken bedenken en op die manier een bredere mogelijkheid hebben van wat je doorstuurt. Je kan dus in tegenstelling tot met MIDI niet slechts getallen tussen de 0 - 127 doorsturen, maar elk getal dat je maar wil. Het grote nadeel van deze manier van seriële communicatie is dat, aangezien je zelf het protocol verzint, je ook zelf aan de ontvangende kant dit protocol moet schrijven. Het werkt dus niet zomaar in een DAW. Binnen Max, P5JS of SuperCollider kan je er echter wel (vrij) makkelijk data mee ontvangen.

Data versturen

Om data te versturen via seriële communicatie moet je eerst de seriële communicatie opstarten door in de setup()-functie Serial.begin(9600) te zetten. De 9600 is de baudrate waarmee je communiceert. Of te wel, hoe snel de data doorgestuurd wordt naar de computer. De Teensy maakt gebruik van deze Arduino functie, maar stuurt data op full-USB-speed door naar de computer, dus eigenlijk kan je elk willekeurig getal invullen, er zal altijd met de maximale snelheid data verstuurd worden. 9600 wordt gebruikt omdat dit bij andere Arduino-devices de standaard is.

Vervolgens kan je, bijvoorbeeld in de loop()-functie data versturen. Dat doe je met Serial.print() en Serial.println(). De ln bij Serial.println() geeft aan dat er een nieuwe regel achter wordt gezet, daarmee kan je dus onderscheid maken tussen verschillende berichten. Een voorbeeld om twee potmeters door te sturen via seriële communicatie kan je hieronder vinden:


int pot1 = A0;
int pot2 = A1;
void setup() { 
    Serial.begin(9600);
    pinMode(pot1, INPUT);
    pinMode(pot2, INPUT);
}

void loop() {
    int potdata1 = analogRead(pot1);
    int potdata2 = analogRead(pot2);
    Serial.print("pot1: ");
    Serial.println(potdata1);
    Serial.print("pot2: ");
    Serial.println(potdata2);
}

Data ontvangen in Max

Om deze seriële data in Max binnen te halen heb je een aantal objecten nodig. Hieronder staat een screenshot van hoe dit werkt.

serialMax


⬆️