Pins & Events in eigenen Erweiterungen

Hallo,

nun mal mit unserem offizellen Forenaccount.

Ich möchte gerne eine eigene Erweiterung schreiben, die einen Block erzeugt, mit dem ich Pins der 26poligen Steckerleiste prüfen und auf deren Level reagieren kann. Also sowas wie schon jetzt unter Input für die Tasten möglich ist: „Wenn Taste A gedrückt“

Ich würde also gerne einen Block haben: „Wenn Pin C4 auf High“.

Im Prinzip hat @klmi sowas schon in seiner ft Erweiterung gemacht: GitHub - MKleinSB/pxt-fischertechnik-calliope: MakeCode Extension for Calliope Mini Fischertechnikboard

Nur leider verstehe ich noch nicht, wieso der Code seiner Erweiterung bei einer Leveländerung an einem Pin ausgeführt wird. Also warum führt das Event dazu, das sein Code in OnPinReleased bzw. OnPinPressed ausgeführt wird. Wie und wo wird das gesteuert?

Außerdem ist mir nicht ganz klar, was die Nummern in der Enumeration von ftPins bedeuten:

 const enum ftPins {
  C16 = 9,   //C16
  C17 = 15,  //C17
  P1 = 7,   //P1
  P2 = 8,   //P2
  P0 = 19, //P0
  P3 = 23, //P3 
  C4 = 10,  //C4
  C5 = 11, //C5
  C6 = 17, //C6
  C7 = 20,  //C7
  C8 = 21,  //C8
  C9 = 22,  //C9
  C10 = 16, //C10
  C11 = 14, //C11
  C12 = 13 //C12
}

Ich habe schon die Schaltpläne und Pinlisten durchgeforstet. Mir ist aber leider nicht klar geworden, was die Zuweisung von z.B. C16 = 9 zu bedeuten hat.

Mir ist schon klar, das sich dieses Problem mit vorhandenen Blöcken lösen lässt, aber ich würde gerne die Hintergründe zu den Events und Pins verstehen.

Kann mir hier bitte jemand weiterhelfen! Oder Links auf eine Doku geben, aus der das hervorgeht.

VG
Martin

1 „Gefällt mir“

Das sind die Nummern aus dem Core für die Pins. War ein Trick damit ich die Erweiterung mit dem Editor vom microbit compilieren konnte. Ist aber durch das neue Makecode nicht mehr notwendig. Jetzt reicht: C16 = digitalpin.c16
Ansonsten muss ich mich selbst nochmal einlesen wenn ich Zeit habe.
Es gibt Events die sowieso auf dem messagebus gemeldet werden, manche muss man aber auch selbst auslösen, indem Du Code im Hintergrund laufen lässt der dann einen Event startet. Schau mal hier: Control

OK, vielen Dank für die Erklärung der Enumeration. Ich war schon etwas verzweifelt, dass ich da nicht durchgestiegen bin.

Zu den Events: Mir ist schon klar, das es Events gibt, die automatisch ausgelöst werden, und welche die ich selber auslösen kann.
Meine Frage ist eher, woher weiß das System, das z.B. bei einem Wechsel von Low auf High an Pin XY genau diese eine meine Funktion aufgerufen werden muss. Ich sehe noch keine Verbindung zwischen eigener Erweiterung und den Systemevents. Und dazu hab ich leider auch noch keine Doku gefunden. Irgendwas muss ich übersehen, denn deine Erweiterung scheint ja zu funktionieren.

@joern.alraun habt Ihr da bei Euch in Berlin nicht jemanden, der mir mal den Schubs in die richtige Richtung geben kann.

Schau mal, hier hatte ich mal einen Eventhandler für ne Lichtschranke gebaut:

const enum zustand {
//% block="interrupted"
unterbrochen = 20,
//% block="not interrupted"
nicht_unterbrochen = 40

}

//% weight=100 color=#0641f9 icon="\uf085"
namespace fischertechnik {

const LightEventID = 3100;
let lastLightLevel = 0;
let Empfindlichkeit = 20;

/**
 * Do something when the phototransistor at the specified pin is interrupted or not.
 * @param pin with phototransistor connected
 * @param LSzustand interrupted or not
 */
//% subcategory="Phototransistor"
//% pin.fieldEditor="gridpicker" 
//% pin.fieldOptions.columns=4
//% blockId=onLightLevel block="phototransistor at pin %pin | is | %LSzustand"
export function onLightLevel(pin: AnalogPin, LSzustand: zustand, handler: () => void) {
    control.onEvent(LightEventID + pin + LSzustand, EventBusValue.MICROBIT_EVT_ANY, handler);
    control.inBackground(() => {
        while (true) {
            const LLevel = pins.analogReadPin(pin);
            if ((LLevel > Empfindlichkeit) && (lastLightLevel <= Empfindlichkeit) && (LSzustand == zustand.unterbrochen)) {
                control.raiseEvent(LightEventID + pin + LSzustand, pin);
            } else if ((LLevel <= Empfindlichkeit) && (lastLightLevel > Empfindlichkeit) && (LSzustand == zustand.nicht_unterbrochen)) {
                control.raiseEvent(LightEventID + pin + LSzustand, pin);
            }
            basic.pause(200);
            lastLightLevel = LLevel
        }
    })
}

/**
* The read phototransistor block reads the pin that a Phototransistor is 
* connected to and returns true or false when the lightlevel ist lower or 
* higher than the lightsensitivity
* @param pin - is the pin which a phototransistor is connected to
* @param LSzustand - if interrupted or not
*/
//% subcategory="Phototransistor"
//% blockId=readPhototransistor
//% block="Phototransistor at %pin| is | %LSzustand"

export function readPhototransistor(pin: AnalogPin, LSzustand: zustand) {
    const LiLevel = pins.analogReadPin(pin);
    let Ergebnis = false;
    if ((LiLevel > Empfindlichkeit) && !(LSzustand == zustand.nicht_unterbrochen)) {
        Ergebnis = true;
    } else if ((LiLevel <= Empfindlichkeit) && !(LSzustand == zustand.unterbrochen)) {
        Ergebnis = true;
    }
    return Ergebnis
}

/**
 * Set the sensitivity (analogvalue) of the photocell when 
 * it´s interrupted. Normaly on Calliope Mini it´s
 * not necessary with original 
 * fischertechnik parts. The predefined value is 20.
 * On micro:bit the lightsensitivity should be 500
 *  
 * @param value - (analogvalue)
 */
//% subcategory="Phototransistor"
//% value.defl=20
//% value.min=5 value.max=1023
//% blockId="SetLightSensitivity" block="set lightsensitivity to %value"
export function SetLightSensitivity(value: number): void {
    Empfindlichkeit = value;
}

}

Du siehst: die EventID ist 3100. Die ABfrage läuft in einer Endlosschleife. Solange TRUE.
Wenn eine bestimmte Helligkeit erreicht ist wird ein Event ausgelöst. Wird zusammengesetzt aus dem Startwert 3100 + vielleicht der Pinnummer bei Dir wenn der Pin kurzgeschlossen auf Low ist.
Ich entwickle immer mit einer Customdatei. Also auf Javascript gehen, Explorer links öffnen, auf + gehen. Ich hab Dir schon eine angelegt, mit der Du mal den ganzen Spaß testen kannst. Das mit dem Pull usw. würde ich an Deiner Stelle vielleicht erstmal mit Blöcken bauen und dann einfügen wenn es läuft.

Viel Erfolg!

Vielleicht setze ich mich morgen mal ran und mache das für Pins.

Hallo Michael,

vielen Dank erstmal für deine Infos. Ich schau mir das heute Abend noch mal in Ruhe an und teste auch ein bisschen. Du beschreibst ja, wie man ein eigenes Event „erfindet“ und sendet.

Aber ich glaube, ich habe mein Problem noch nicht so beschrieben, das Ihr wisst was ich meine.

Wenn du dir deine Erweiterung pxt-fischertechnik-calliope ansiehst, dann gibt es darin den folgenden Quelltext in der main.ts:

/**
* Do something when one of the pins is pressed.
* Use PinAsSwitch first!
* @param pin to be checked
*/
//% blockId="OnPinPressed" block="wenn Pin %ftpin | gedrückt"
//% ftpin.fieldEditor="gridpicker" ftpin.fieldOptions.columns=5
//% weight=82 blockGap=8
export function OnPinPressed(ftpin: ftPins, handler: Action) {
	const pin = <DigitalPin><number>ftpin;
	pins.onPulsed(pin, <number>pushType.down, handler);
}

Dieser Code sorgt für den Block „wenn Pin XY gedrückt“.

Meine Frage: Woher weiß das System, das genau diese Funktion OnPinPressed() aufgerufen werden muss, wenn z.B. der Pin C16 auf Masse gezogen wird.

Und jetzt wo ich den Text hier schreibe, fällt es mir - glaube ich - wie Schuppen von den Augen :slight_smile:

Die Funktion OnPinPressed() hat als Parameter einen handler/Action. Das ist ja der Programmteil, der später vom Anwender in Makecode in diesen Block programmiert wird.
Und genau diese eigene Programmierung wird dann über die Funktion pins.onPulsed() an den Core durchgereicht - und zwar mit der Bedingung, das der Code nur ausgeführt werden soll, wenn pushType = down ist. Das ist die Verknüpfung mit dem Event!

Funktionen als Parameter (Callbackfunktionen) waren noch nie mein Ding … wird Zeit, dass ich mich damit noch mal beschäftige :slight_smile:

VG
Martin

1 „Gefällt mir“

Ich werd wahnsinnig. Kann mir bitte jemand sagen, warum mein folgendes Programm bei C8/Key.D nicht funktioniert?

Ich kann C9/C7/C6 gegen GND kurzschließen und die RGB LED wird entsprechend farbig. Nur mit meinem Code in der custom.ts und Pin C8 geht es nicht.

Ich kapier es einfach nicht …

ACHTUNG: Das geht nicht im Simulator!

OK, es klappt nun hier in einem neuen Testprogramm. Die Events scheinen nicht zu kommen, wenn man das Display ausschaltet!!! Ich hatte aber im Kopf, das man die Pins auf der 26 poligen Stiftleiste, die auch mit LEDs gekoppelt sind, nur dann nutzen kann, wenn man dazu das Display ausschaltet. Das Gegenteil ist der Fall. Danke an @klmi, der den entscheidenden Hinweis gegeben hat. Das muss früher wohl mal anders gewesen sein.

Blöd ist halt, das nun bei jedem Schaltvorgang wild irgendwelche LEDs im Display aufleuchten. Außerdem wird beim Start des Calliope das Herz nicht korrekt angezeigt, sobald man einen Block aus der Custom.ts nutzt, der auf ein Event eines Pins reagiert.

Sind diese Anzeigeproblme jetzt ein bug oder feature? Wo sind hier die Makecode Experten?

Hier kann man sich ansehen, wie ich es jetzt gelöst habe: GamepadDevelop_v2