Für ein größeres Unterrichtsprojekt benötige ich mehrere Touchsensoren am Calliope. Meine Wahl fiel daher auf den Seeedstudio Grove I2C Touch Sensor, der den MPR121 Chip von Freescale verwendet. Er unterstützt bis zu 12 Fühler. Der gleiche Chip ist auch auf den Sensorboards von Adafruit und Sparkfun verbaut, allerdings liefert Seeedstudio bereits vier per Kabel angeschlossene Fühler mit. Das passt vom Gesamtkonzept her in ein Grove Sensorset für die Schule gut rein.
FallstrickeDas Hackster-Projekt von HHF mit einem Calliope und dem MPR121 Touch Pad von Sparkfun war sehr verlockend - HHF hat sich die Mühe gemacht und die Initalisierungsbefehle für den MPR121 aus den Sparkfun Demos nach PXT in JavaScript portiert. Wie er vorgegangen ist, kann anschaulich auf seiner Projektseite nachgelesen werden. Leider funktionieren die Initialisierungsbefehle jedoch nicht richtig für das Seeedstudio Sensorboard :-(
Eine weitere Netzsuche lieferte ein Projekt mit MicroPython, micro:bit, und dem MPR121-Sensorboard von Adafruit. Thinkberg hat kurzerhand den Code der Seite nach PXT in JavaScript portiert. Getestet und bäm! - läuft. Glücklich. Vielen Dank!
Die Initialisierungsbefehle unterscheiden sich teilweise, den entscheidenden Diff habe ich aber noch nicht entdeckt. Wenn ich ihn noch finde, reiche ich ihn in den Kommentaren nach.
SchnelltestWenn du den Grove I2C Touch Sensor an den linken Grove Sensor auf dem Calliope mini angeschlossen hast (nur dort kann das I2C-Protokoll verwendet werden), klicke auf pxt.calliope.cc auf Javascript und füge den folgenden Code dort ein:
let CONFIG2 = 0
let CONFIG1 = 0
let DEBOUNCE = 0
let FDLT = 0
let NCLT = 0
let NHDT = 0
let FDLF = 0
let NCLF = 0
let NHDF = 0
let MHDF = 0
let FDLR = 0
let NCLR = 0
let NHDR = 0
let MHDR = 0
let ECR = 0
let SOFTRESET = 0
let RELEASETH_0 = 0
let TOUCHTH_0 = 0
let ADDRESS = 0
basic.forever(() => {
let bits2 = touched();
if (bits2 != 0) {
serial.writeString("touched: ")
for (let e = 0; e <= 12 - 1; e++) {
let touched = isTouched(bits2, e)
serial.writeString(touched ? "1": "0")
if (touched) {
led.plot(e % 5, e / 5)
} else {
led.unplot(e % 5, e / 5)
}
}
serial.writeLine("")
}
})
ADDRESS = 90
TOUCHTH_0 = 65
RELEASETH_0 = 66
SOFTRESET = 128
ECR = 94
MHDR = 43
NHDR = 44
NCLR = 45
FDLR = 46
MHDF = 47
NHDF = 48
NCLF = 49
FDLF = 50
NHDT = 51
NCLT = 52
FDLT = 53
DEBOUNCE = 91
CONFIG1 = 92
CONFIG2 = 93
function mpr121Command(register: number, value: number) {
pins.i2cWriteNumber(ADDRESS, (register << 8) | value, NumberFormat.UInt16BE);
}
function setThresholds(touch: number, release: number) {
for (let i = 0; i <= 12; i++) {
mpr121Command(TOUCHTH_0 + 2 * i, touch);
mpr121Command(RELEASETH_0 + 2 * i, release);
}
}
function touched() {
mpr121Command(0, 0);
return pins.i2cReadNumber(ADDRESS, NumberFormat.UInt16BE);
}
function isTouched(bits: number, electrode: number): boolean {
return (bits & (1 << electrode)) > 0;
}
// reset
mpr121Command(SOFTRESET, 0x63);
basic.pause(1)
// set thresholds
mpr121Command(ECR, 0x00);
setThresholds(12, 6);
// configuration
mpr121Command(MHDR, 0x01);
mpr121Command(NHDR, 0x01);
mpr121Command(NCLR, 0x0E);
mpr121Command(FDLR, 0x00);
mpr121Command(MHDF, 0x01);
mpr121Command(NHDF, 0x05);
mpr121Command(NCLF, 0x01);
mpr121Command(FDLF, 0x00);
mpr121Command(NHDT, 0x00);
mpr121Command(NCLT, 0x00);
mpr121Command(FDLT, 0x00);
mpr121Command(DEBOUNCE, 0x00);
mpr121Command(CONFIG1, 0x10);
mpr121Command(CONFIG2, 0x20);
mpr121Command(ECR, 0x8f);
Der Beispielcode übermittelt über die serielle Schnittstelle eine 12-stellige Bitfolge. Jedes Bit steht für einen der Fühler. Wird ein Fühler berührt, wird eine 1 ausgegeben, ansonsten eine 0. Zusätzlich aktiviert der Calliope auf seinem Display einzelne Pixel, wenn die Fühler berührt werden.
Sensor-PuzzleFür das Unterrichtsprojekt habe ich ein PXT Package geschrieben, mit dem die Initialisierung des Grove I2C Touch Sensors sowie das Abfragen des momentan berührten Fühlers mit Blöcken implementiert werden können. Das sieht aktuell so aus:
Für die Spoiler-Videos ganz oben sehen die Block-Skripte so aus:
Tonleiter (Blöcke)
Sprite-Animation (Blöcke)
Und so in Javascript:
Tonleiter (JavaScript)
basic.forever(() => {
if (grove_mpr121.touchedFeeler() == 0) {
music.playTone(262, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 1) {
music.playTone(294, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 2) {
music.playTone(330, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 3) {
music.playTone(349, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 8) {
music.playTone(392, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 9) {
music.playTone(440, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 10) {
music.playTone(494, music.beat(BeatFraction.Whole))
}
if (grove_mpr121.touchedFeeler() == 11) {
music.playTone(523, music.beat(BeatFraction.Whole))
}
})
grove_mpr121.init(
)
Sprite-Animation (JavaScript)
let Platzhalter: game.LedSprite = null
basic.forever(() => {
if (grove_mpr121.touchedFeeler() == 0) {
Platzhalter.change(LedSpriteProperty.X, -1)
}
if (grove_mpr121.touchedFeeler() == 1) {
Platzhalter.change(LedSpriteProperty.X, 1)
}
if (grove_mpr121.touchedFeeler() == 2) {
Platzhalter.change(LedSpriteProperty.Y, 1)
}
if (grove_mpr121.touchedFeeler() == 3) {
Platzhalter.change(LedSpriteProperty.Y, 1)
}
if (grove_mpr121.touchedFeeler() == 8) {
Platzhalter.change(LedSpriteProperty.Brightness, -5)
}
if (grove_mpr121.touchedFeeler() == 9) {
Platzhalter.change(LedSpriteProperty.Brightness, 5)
}
if (grove_mpr121.touchedFeeler() == 10) {
Platzhalter.change(LedSpriteProperty.Blink, -5)
}
if (grove_mpr121.touchedFeeler() == 10) {
Platzhalter.change(LedSpriteProperty.Blink, 5)
}
})
grove_mpr121.init(
)
Platzhalter = game.createSprite(2, 2)
Weiterführende Hinweise- Der Seeedstudio Grove I2C Touch Sensor wird mit 4 Fühlern geliefert (0-3). Vier weitere können ebenfalls per Stecker angeschlossen werden (8-11). Für vier weitere müssen zuerst Steckleisten aufgelötet werden (4-7).
- Der Block "welcher Fühler wird berührt?" liefert -2, wenn kein Fühler berührt wird, -1, wenn mehr als ein Fühler gleichzeitig berührt wird, oder 0-11, je nach aktivierten Fühler.
- Multitouch könnte genauer abgefragt werden, steht aber weiter hinten auf meiner ToDo-Liste.
- Das Adafruit MPR121 breakout sollte auch mit den Blöcken am Calliope funktionieren. Muss noch getestet werden, weiter vorne auf meiner ToDo-Liste.
- Falls dich die Initialisierung interessiert:
Diese Initialisierung ist für das Sparkfun Sensorboard:
let regval: number[] = [0x2B01, 0x2C01, 0x2D00, 0x2E00, 0x2F01, 0x3001, 0x31FF,
0x3202, 0x4106, 0x420A, 0x4306, 0x440A, 0x4506, 0x460A,
0x4706, 0x480A, 0x4906, 0x4A0A, 0x4B06, 0x4C0A, 0x4D06,
0x4E0A, 0x4F06, 0x500A, 0x5106, 0x520A, 0x5306, 0x540A,
0x5506, 0x560A, 0x5706, 0x580A, 0x5D04, 0x5E0C]
for (let i = 0; i < regval.length; i++) {
pins.i2cWriteNumber(ADDRESS, regval[i], NumberFormat.UInt16BE)
}
Und diese Initialisierung für Adafruit und Seeedstudio:
pins.i2cWriteNumber(ADDRESS, 0x8063, NumberFormat.UInt16BE)
basic.pause(1)
pins.i2cWriteNumber(ADDRESS, 0x5E00, NumberFormat.UInt16BE)
let regval: number[] = [0x4106, 0x420C, 0x4306, 0x440C, 0x4506, 0x460C, 0x4706,
0x480C, 0x4906, 0x4A0C, 0x4B06, 0x4C0C, 0x4D06, 0x4E0C,
0x4F06, 0x500C, 0x5106, 0x520C, 0x5306, 0x540C, 0x5506,
0x560C, 0x5706, 0x580C, 0x2B01, 0x2C01, 0x2D0E, 0x2E00,
0x2F01, 0x3005, 0x3101, 0x3200, 0x3300, 0x3400, 0x3500,
0x5B00, 0x5C10, 0x5D20, 0x5E8F]
for (let i = 0; i < regval.length; i++) {
pins.i2cWriteNumber(ADDRESS, regval[i], NumberFormat.UInt16BE)
}
Comments