Bitte geben Sie einen Grund für die Verwarnung an
Der Grund erscheint unter dem Beitrag.Bei einer weiteren Verwarnung wird das Mitglied automatisch gesperrt.
Projekt: "Jetzt wird es endlich fertig gemacht...."
#1

So, es wird endlich einmal Zeit an meinen Spieltisch weiter zu arbeiten. Ich hatte vor Jahren angefangen damit, dann für die Steuerung der Register Kippschalter provisorisch verbaut. Das hat nie so wirklich perfekt funktioniert und aufgrund der mechanischen Schalter war es auch nicht sonderlich flexibel und dazu noch recht hässlich. Nicht einmal die Frontverkleidung hat es die letzten Jahre an die Orgel geschafft...
So sieht das Trauerspiel nun schon seit Monaten aus:
Bild entfernt (keine Rechte)
Nun habe ich mir Gedanken gemacht und bin zu diesem Ergebnis gekommen:
1. Mechanische Schalter müssen weg!
2. Front muss verkleidet werden
Punkt zwei ist klar und wohl das einfachste. Für Punkt eins habe ich nun lange überlegt. Eine Idee waren Taster mit Beleuchtung. Praktisch ist der Spaß aber recht aufwändig und teuer. Eigentlich wäre mir eine Touchsteuerung am liebsten und auch sehr viel flexibler als beschriftete Taster. Also habe ich einmal geschaut was es gibt. Die erste Idee war einfach ein Touchmonitor mit HDMI anschließen und über die GrandOrgue Oberfläche steuern. Praktisch mag dies bei Sets wie der Silbermann Orgel ( https://www.prospectum.com/index.php?lang=en&id1=2&id2=8 ) dank der Consolen Ansicht super funktionieren, aber die meisten anderen Sets sind da wohl nicht sinnvoll zu bedienen.
Dann bin ich auf einen Monitor gestoßen mit einem ESP32:
https://www.berrybase.de/waveshare-esp32...2kb-sram-240mhz
Davon habe ich mir nun einmal zwei Stück bestellt. Die Idee ist eine eigene Ansicht zu schreiben, die über einfache Buttons mit Aufschrift dann ein Midi Signal sendet. Den Anbieter kenne ich bereits und habe gute Erfahrungen mit seiner Hardware gemacht, leider habe ich zu USB Host und so weiter keine wirklichen Informationen gefunden. Laut diversen Reddit Posts soll es aber funktionieren. Wenn nicht, dann bleibt ja noch UART, Seriell oder etwas anderes.
Soweit jetzt die Grundidee, die Hardware ist bestellt und dann schaue ich einmal was sich damit machen lässt und berichte hier. Zwei Geräte habe ich bestellt, weil eines dann wirklich jungfreulich an den Spieltisch soll und ein zweites ist zum Programmieren und Experimentieren. Vielleicht nutze ich auch irgendwann beide... Zumindest sind 40 Euro pro Gerät überschaubar.
Vielleicht eine Frage in die Runde: Wie sind eure Erfahrungen und Einschätzungen für die Kommunikation über Midi mit dem System bei einem ESP32? Bevor ich mit einem analogen Midi anfange würde ich den Controller einfach mit meinem Pi Pico oder Arduino verbinden um die Signale von dort zu senden.
Kann es sein, dass du dir versucht Midistops nachzubauen? Nutzt du Linux für dein Grandorgue?
7" sind auch nicht wirklich groß. Kommst du da nicht besser mit einem großen, normalen Touchscreen und dann einem "Linux-Midistops", das auf einem Raspi oder direkt deinem VPO-Riegel läuft?
#3

Es soll im Prinzip für jedes Werk einen Abschnitt geben in dem die Register als Button angezeigt werden können, wie in dem ersten Beispiel hier https://docs.lvgl.io/master/details/widg...ttonmatrix.html
7" sollte eigentlich ausreichen, wenn nicht dann kann man ja einen zweiten zusätzlich verbauen. Bei meinen Sets ist das aber unwahrscheinlich. Über einen Reiter würde ich noch weitere Funktionen wie extra Koppeln, Aufnahme, aktive Orgel und so weiter haben wollen.
Mag sein, dass es wie Midistops ist, aber es ist eben dann eine eigene Lösung.
#4

#5

Also, ich hatte mir die Sache etwas einfacher vorgestellt. Das Arbeiten mit einem Display ist doch sehr viel komplexer als ich geahnt habe. Lvgl ist unheimlich komplex und wirklich nichts für ambitionierte Hobbyanwender, dazu ist die Dokumentation der Hardware alles andere als gut. Keines der Beispiele ließ sich erstellen.
Nach vielen Stunden habe ich dann endlich eine Lösung mit LovyanGFX gefunden und danach wurde es immer klarer wie es funktioniert. Jetzt bin ich mit LovyanGFX + LVGL8.x am experimentieren. Es funktioniert zumindest schon einmal diverse Schaltflächen zu erstellen und anzuzeigen. Das beste ist aber, es lässt sich compilieren :)
Naja, schön ist aber anders:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
Simple Demo LovyanGFX + LVGL8.x
*/
#define LGFX_AUTODETECT // Autodetect board
#define LGFX_USE_V1 // set to use new version of library
//#define LV_CONF_INCLUDE_SIMPLE
/* Uncomment below line to draw on screen with touch */
//#define DRAW_ON_SCREEN
#include <LovyanGFX.hpp> // main library
static LGFX lcd; // declare display variable
#include <lvgl.h>
//#include "lv_conf.h"
/*** Setup screen resolution for LVGL ***/
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * 10];
// Variables for touch x,y
#ifdef DRAW_ON_SCREEN
static int32_t x, y;
#endif
/*** Function declaration ***/
void display_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
void touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data);
void lv_button_demo(void);
void setup(void)
{
Serial.begin(115200); /* prepare for possible serial debug */
lcd.init(); // Initialize LovyanGFX
lv_init(); // Initialize lvgl
// Setting display to landscape
if (lcd.width() < lcd.height())
lcd.setRotation(lcd.getRotation() ^ 1);
/* LVGL : Setting up buffer to use for display */
lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * 10);
/*** LVGL : Setup & Initialize the display device driver ***/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = display_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*** LVGL : Setup & Initialize the input device driver ***/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
lv_indev_drv_register(&indev_drv);
/*** Create simple label and show LVGL version ***/
String LVGL_Arduino = "WT32-SC01 with LVGL ";
LVGL_Arduino += String('v') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
lv_obj_t *label = lv_label_create(lv_scr_act()); // full screen as the parent
lv_label_set_text(label, LVGL_Arduino.c_str()); // set label text
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 20); // Center but 20 from the top
lv_button_demo();
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay(5);
#ifdef DRAW_ON_SCREEN
/*** Draw on screen with touch ***/
if (lcd.getTouch(&x, &y))
{
lcd.fillRect(x - 2, y - 2, 5, 5, TFT_RED);
lcd.setCursor(380, 0);
lcd.printf("Touch:(%03d,%03d)", x, y);
// }
#endif
}
/*** Display callback to flush the buffer to screen ***/
void display_flush(lv_disp_drv_t * disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
lcd.startWrite();
lcd.setAddrWindow(area->x1, area->y1, w, h);
lcd.pushColors((uint16_t *)&color_p->full, w * h, true);
lcd.endWrite();
lv_disp_flush_ready(disp);
}
/*** Touchpad callback to read the touchpad ***/
void touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
uint16_t touchX, touchY;
bool touched = lcd.getTouch(&touchX, &touchY);
if (!touched)
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touchX;
data->point.y = touchY;
// Serial.printf("Touch (x,y): (%03d,%03d)\n",touchX,touchY );
}
}
/* Counter button event handler */
static void counter_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *btn = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED)
{
static uint8_t cnt = 0;
cnt++;
/*Get the first child of the button which is the label and change its text*/
lv_obj_t *label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
LV_LOG_USER("Clicked");
Serial.println("Clicked");
}
}
/* Toggle button event handler */
static void toggle_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED)
{
LV_LOG_USER("Toggled");
Serial.println("Toggled");
}
}
void lv_button_demo(void)
{
lv_obj_t *label;
// Button with counter
lv_obj_t *btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, counter_event_handler, LV_EVENT_ALL, NULL);
lv_obj_set_pos(btn1, 100, 100); /*Set its position*/
lv_obj_set_size(btn1, 120, 50); /*Set its size*/
label = lv_label_create(btn1);
lv_label_set_text(label, "Button");
lv_obj_center(label);
// Toggle button
lv_obj_t *btn2 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn2, toggle_event_handler, LV_EVENT_ALL, NULL);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
lv_obj_set_pos(btn2, 250, 100); /*Set its position*/
lv_obj_set_size(btn2, 120, 50); /*Set its size*/
label = lv_label_create(btn2);
lv_label_set_text(label, "Toggle Button");
lv_obj_center(label);
}
#6

Okay, vermutlich hatte @Soubasse doch recht. Der Aufwand für meine Idee ist sehr hoch und ich werde jetzt einen anderen Weg gehen. Meist nutze ich ja Sweelinq, es wäre also praktisch die Oberfläche direkt nutzen zu können. Also wäre die simple Darstellung über VNC doch eine gute Idee oder? VNC auf einem Microcontroller wäre aber absurd? Naja, andere hatten die Idee auch schon https://github.com/Links2004/arduinoVNC da ich LovyanGFX schon am Laufen habe, funktioniert es sehr schnell. WLAN Daten noch eingetragen und die eigene PIN Belegung und schon wurde mein Desktop dargestellt.
Das wäre nun meine bevorzugte Methode für den Alltag und für alle Fälle und GrandOrgue kann ich meine Oberfläche ja weiter schreiben nebenbei. Ein Button zum Wechseln ist ja keine Wissenschaft.
#8

Midistops war damals eine gute Sache, Heute ist das Konzept aber eher überholt. Schalter und Knöpfe lassen sich einfach von so gut wie jedem realisieren, Sweelinq und Hauptwerk bieten beide guten Touch Support, einfache Tablets zum bedienen über eine Remoteanzeige gibt es bei Kleinanzeigen für weniger als 50 Euro.
Daher sehe ich die Notwendigkeit heute nicht mehr. Nur GrandOrgue ist da leider eine Ausnahme. Natürlich funktioniert dort auch Touch, aber nur wenige Sets haben eine dafür optimierte Oberfläche. Ehrlich gesagt fällt mir gerade nur die Silbermann Orgel ein.
Zitat von Soubasse im Beitrag #7
Oder du entwirfst eine Art Midistops für Linux und gehst komplett auf VNC. Komplexität rausnehmen, Prozesse standardisieren ;-)
Das wäre nichts für mich. Wenn ich die letzten Tage etwas wieder einmal lernen musste, dann dass ich ein guter Programmierer bin, aber miserabel beim Thema Benutzerschnittstellen erstellen. Nicht ohne Grund haben alle ernsthaften Projekte Fachpersonal die sich nur mit der Anordnung eines Buttons 40 Stunden die Woche beschäftigen.

Zitat von Christian_Hofmann im Beitrag #8
Nicht ohne Grund haben alle ernsthaften Projekte Fachpersonal die sich nur mit der Anordnung eines Buttons 40 Stunden die Woche beschäftigen.
Bei allen Projekten, die Designer benötigen, gibt es unterschiedliche Gründe:
- die Entwickler haben kein Händchen für Design
- den Entwicklern ist es egal
- fehlendes einheitliches Bedienkonzept
- Zeitdruck/keine Zeit für optimierte Oberflächen
- unvernünftige Vorgaben - "es muss alles in einem Dialog untergebracht werden"
- und, und, und...
Bei Hauptwerk, GrandOrgue und Tools finden sich so einige Grausamkeiten

#10

Zitat von Montre im Beitrag #9
Bei Hauptwerk, GrandOrgue und Tools finden sich so einige Grausamkeiten
Ich betreibe ja von einigen Kirchengemeinden die Webseite und kümmere mich um die Gemeindebriefe. Nicht ohne Grund habe ich ein festes Layout vorgegeben und sämtliche Dinge reglementiert. Auf einer Seite der Landeskirche landen sonst schnell Beiträge mit fünf verschiedenen Schriftarten und zehn verschiedenen Farben in einen Absatz...
So lange niemand klare Vorgaben macht und die Umsetzung durchsetzt hat man eben immer ein Wildwuchs... Ich erinnere mich noch an die Software der 90er Jahre... Brrr
#11

So langsam habe ich den Dreh raus mit lvgl. Dumm wenn man sich Stunden mit der Gestaltung einfacher Buttons befasst um dann zu begreifen, dass es sehr leistungsfähige GUI Builder dazu gibt...
Bild entfernt (keine Rechte)
Bild entfernt (keine Rechte)
Erstaunlich was ein Microcontroller so alles kann. Tabs, Scrolling und so weiter und sogar in guten Tempo. Wobei ich keine Wischgesten beim Orgeln gebrauchen kann. Irgendwie lässt mich die Idee einer eigenen Touchoberfläche nicht los.
#12

So, mein Grundgerüst ist fertig und funktioniert. Die nächsten Schritte wären:
Code aufräumen
- Verschiedene Ansichten (Tabview), ungenutzte Buttons nicht anzeigen
- Informationen auf SD Karte ablegen und von dort abrufen. Eine einfache Möglichkeit die Daten zu übertragen (ftp?), einen Editor der ein Erstellen der Oberfläche direkt am Display ermöglichst.
- Auf Midi Eingänge reagieren (Stichwort Setzer) und entsprechend dies zu übernehmen. Display deaktivieren wenn Orgel nicht bereit (Bootvorgang, neue Orgel laden, etc). Display Helligkeit bei Inaktivität senken. Vielleicht mit Sensor ermitteln ob jemand an der Orgel sitzt?
Hier übrigens das bisherige Konstrukt

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <driver/i2c.h>
#include <lvgl.h>
//#include "lv_conf.h"
#include "USB.h"
#include "USBMIDI.h"
USBMIDI MIDI;
class LGFX : public lgfx::LGFX_Device
{
public:
lgfx::Bus_RGB _bus_instance;
lgfx::Panel_RGB _panel_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_GT911 _touch_instance;
LGFX(void)
{
{
auto cfg = _panel_instance.config();
cfg.memory_width = 800;
cfg.memory_height = 480;
cfg.panel_width = 800;
cfg.panel_height = 480;
cfg.offset_x = 0;
cfg.offset_y = 0;
_panel_instance.config(cfg);
}
{
auto cfg = _panel_instance.config_detail();
cfg.use_psram = 1;
_panel_instance.config_detail(cfg);
}
{
auto cfg = _bus_instance.config();
cfg.panel = &_panel_instance;
cfg.pin_d0 = GPIO_NUM_14; // D0
cfg.pin_d1 = GPIO_NUM_38; // D1
cfg.pin_d2 = GPIO_NUM_18; // D2
cfg.pin_d3 = GPIO_NUM_17; // D3
cfg.pin_d4 = GPIO_NUM_10; // D4
cfg.pin_d5 = GPIO_NUM_39; // D5
cfg.pin_d6 = GPIO_NUM_0; // D6
cfg.pin_d7 = GPIO_NUM_45; // D7
cfg.pin_d8 = GPIO_NUM_48; // D8
cfg.pin_d9 = GPIO_NUM_47; // D9
cfg.pin_d10 = GPIO_NUM_21; // D10
cfg.pin_d11 = GPIO_NUM_1; // D11
cfg.pin_d12 = GPIO_NUM_2; // D12
cfg.pin_d13 = GPIO_NUM_42; // D13
cfg.pin_d14 = GPIO_NUM_41; // D14
cfg.pin_d15 = GPIO_NUM_40; // D15
cfg.pin_henable = GPIO_NUM_5; // DE
cfg.pin_vsync = GPIO_NUM_3; // VSYNC
cfg.pin_hsync = GPIO_NUM_46; // HSYNC
cfg.pin_pclk = GPIO_NUM_7; // PCLK
cfg.freq_write = 16000000; // 16 MHz
cfg.hsync_polarity = 0;
cfg.hsync_front_porch = 8;
cfg.hsync_pulse_width = 4;
cfg.hsync_back_porch = 8;
cfg.vsync_polarity = 0;
cfg.vsync_front_porch = 8;
cfg.vsync_pulse_width = 4;
cfg.vsync_back_porch = 8;
cfg.pclk_idle_high = 0;
_bus_instance.config(cfg);
}
_panel_instance.setBus(&_bus_instance);
{
auto cfg = _light_instance.config();
cfg.pin_bl = GPIO_NUM_2; // Anpassen, falls notwendig
_light_instance.config(cfg);
}
_panel_instance.light(&_light_instance);
{
auto cfg = _touch_instance.config();
cfg.x_min = 0;
cfg.y_min = 0;
cfg.bus_shared = false;
cfg.offset_rotation = 0;
// I2C connection
cfg.i2c_port = I2C_NUM_0;
cfg.pin_sda = GPIO_NUM_8;
cfg.pin_scl = GPIO_NUM_9;
cfg.pin_int = GPIO_NUM_NC;
cfg.pin_rst = GPIO_NUM_38;
cfg.x_max = 800;
cfg.y_max = 480;
cfg.freq = 100000;
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
LGFX lcd;
static const uint16_t screenWidth = 800;
static const uint16_t screenHeight = 480;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * 10];
/*** Function declaration ***/
void display_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
void touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data);
void lv_button_demo(void);
static int32_t x, y, number;
static int32_t testcounter = 0;
// Globale Variablen für Labels, Buttons und Button-Texte
lv_obj_t *feld1_label;
lv_obj_t *feld2_label;
lv_obj_t *feld3_label;
lv_obj_t *feld1_buttons[14];
lv_obj_t *feld2_buttons[9];
lv_obj_t *feld3_buttons[9];
// **Arrays für Button-Texte definieren (global)**
const char *feld1_button_texts[] = { // Texte für Feld 1 (7 Zeilen x 2 Spalten = 14 Texte)
"Prestant 16.", "Prestant 8.", "Violoncel 8.", "Octaaf 4.", "Quint 3.", "Mixtuur", "Trompet 8.",
"Bordon 16.", "Roerfluit 4.", "Quintflutt 6.", "Rohrflutt 4.", "Octaaf 2.", "Cornet", "ZW - HW"
};
const char *feld2_button_texts[] = { // Texte für Feld 2 (7 Zeilen x 2 Spalten, aber nur 9 genutzt)
"Holpyp 8.", "Gamba 8.", "Flute Okt. 4.", "Gemshoorn 2.", "Flute Harm. 8.", "Vox Celeste 8.",
"Violine 4.", "Basson-hobo 8.", "Tremulant", " ", " ", " ", " ", " " // Nur 9 Texte definiert für Feld 2
};
const char *feld3_button_texts[] = { // Texte für Feld 3 (7 Zeilen x 2 Spalten, aber nur 9 genutzt)
"Contrabass 16.", "Octaaf 8.", "Bazuin 16.", "Tacet", "Subbas 16.", "Fluit 4.",
"Trombine 8.", "HW-PED", "ZW-PED", " ", " ", " ", " ", " ", " " // Nur 9 Texte definiert für Feld 3
};
// Arrays für MIDI-Note-Codes definieren
const byte feld1_midi_notes[] = { // MIDI Noten für Feld 1
48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61
};
const byte feld2_midi_notes[] = { // MIDI Noten für Feld 2
62, 63, 64, 65, 66, 67, 68,
69, 70, 80, 81, 82, 83, 84
};
const byte feld3_midi_notes[] = { // MIDI Noten für Feld 3
71, 72, 73, 74, 75, 76, 77,
78, 79, 85, 86, 87, 88, 89
};
static void button_event_handler(lv_event_t * e);
// Funktionsdeklarationen für MIDI-Sende Funktionen
void sendNoteON(byte note);
void sendNoteOFF(byte note);
void setup()
{
Serial.begin(115200);
lcd.init();
lv_init();
lcd.setRotation(0);
lcd.fillScreen(TFT_BLACK);
/* LVGL : Setting up buffer to use for display */
lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * 10);
/*** LVGL : Setup & Initialize the display device driver ***/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = display_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*** LVGL : Setup & Initialize the input device driver ***/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
lv_indev_drv_register(&indev_drv);
// Midi starten und Oberfläche bauen
MIDI.begin();
USB.begin();
lv_button_demo();
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay(5);
lv_tick_inc(10);
}
// ==========================================================================0
/*** Display callback to flush the buffer to screen ***/
void display_flush(lv_disp_drv_t * disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
lcd.startWrite();
lcd.setAddrWindow(area->x1, area->y1, w, h);
lcd.pushColors((uint16_t *)&color_p->full, w * h, true);
lcd.endWrite();
lv_disp_flush_ready(disp);
}
/*** Touchpad callback to read the touchpad ***/
void touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
uint16_t touchX, touchY;
bool touched = lcd.getTouch(&touchX, &touchY);
if (!touched)
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touchX;
data->point.y = touchY;
//Serial.printf("Touch (x,y): (%03d,%03d)\n",touchX,touchY );
}
}
/* Counter button event handler */
static void counter_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *btn = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED)
{
static uint8_t cnt = 0;
cnt++;
/*Get the first child of the button which is the label and change its text*/
lv_obj_t *label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
LV_LOG_USER("Clicked");
//Serial.println("Clicked");
}
}
/* Toggle button event handler */
void lv_button_demo(void) {
lv_obj_t *label;
// Layout Parameter für Fullscreen-Füllung
int button_breite = 125; // Einheitliche Button-Breite (breiter für Fullscreen)
int button_hoehe = 55; // Einheitliche Button-Höhe (höher für Fullscreen)
int button_abstand_x = 5; // Horizontaler Abstand
int button_abstand_y = 4; // Vertikaler Abstand
int start_x = 10; // Start X-Position (etwas Rand links)
int start_y_labels = 15; // Start Y-Position für Labels (weiter oben)
int start_y_buttons = 45; // Start Y-Position für erste Button-Reihe (unter Labels)
int label_abstand_y = 10; // Vertikaler Abstand zwischen Labels und Buttons
int bottom_space = 80; // Platz am unteren Bildschirmrand für Erweiterungen
// Labels für Feld 1, Feld 2, Feld 3
feld1_label = lv_label_create(lv_scr_act());
lv_label_set_text(feld1_label, "Hoofdwerk");
lv_obj_align(feld1_label, LV_ALIGN_TOP_LEFT, start_x + (button_breite + button_abstand_x) * 0.5, start_y_labels); // Zentriert über Spalte A+B
feld2_label = lv_label_create(lv_scr_act());
lv_label_set_text(feld2_label, "Zwelwerk");
lv_obj_align(feld2_label, LV_ALIGN_TOP_LEFT, start_x + (button_breite + button_abstand_x) * 3 + 20, start_y_labels); // Zentriert über Spalte C+D, +20px Abstand
feld3_label = lv_label_create(lv_scr_act());
lv_label_set_text(feld3_label, "Pedaal");
lv_obj_align(feld3_label, LV_ALIGN_TOP_LEFT, start_x + (button_breite + button_abstand_x) * 6 + 40, start_y_labels); // Zentriert über Spalte E+F
// Buttons erstellen in 7 Zeilen und 6 "Spalten" (A, B, C, D, E, F)
for (int zeile = 0; zeile < 7; zeile++) {
for (int spalte = 0; spalte < 6; spalte++) {
lv_obj_t *btn;
byte midi_note; // Variable für MIDI Note Code
int text_index_in_field = zeile; // Index innerhalb des jeweiligen Feld-Arrays
if (spalte < 2) { // Spalten A und B (Feld 1)
btn = feld1_buttons[zeile] = lv_btn_create(lv_scr_act());
label = lv_label_create(btn);
lv_label_set_text(label, feld1_button_texts[text_index_in_field + spalte * 7]); // Korrekter Index für Feld 1
// MIDI Note aus Array feld1_midi_notes holen
midi_note = feld1_midi_notes[text_index_in_field + spalte * 7]; // Korrekter Index für Feld 1
} else if (spalte < 4) { // Spalten C und D (Feld 2)
btn = feld2_buttons[zeile] = lv_btn_create(lv_scr_act());
label = lv_label_create(btn);
lv_label_set_text(label, feld2_button_texts[text_index_in_field + (spalte - 2) * 7]); // Korrekter Index für Feld 2
// MIDI Note aus Array feld2_midi_notes holen
midi_note = feld2_midi_notes[text_index_in_field + (spalte - 2) * 7]; // Korrekter Index für Feld 2
} else { // Spalten E und F (Feld 3)
btn = feld3_buttons[zeile] = lv_btn_create(lv_scr_act());
label = lv_label_create(btn);
lv_label_set_text(label, feld3_button_texts[text_index_in_field + (spalte - 4) * 7]); // Korrekter Index für Feld 3
// MIDI Note aus Array feld3_midi_notes holen
midi_note = feld3_midi_notes[text_index_in_field + (spalte - 4) * 7]; // Korrekter Index für Feld 3
}
// MIDI Note als Benutzerdaten an Button anhängen (typ-sicher mit uintptr_t)
lv_obj_set_user_data(btn, (void*)(uintptr_t)midi_note); // Cast to uintptr_t, dann void*
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
lv_obj_set_size(btn, button_breite, button_hoehe);
lv_obj_set_pos(btn, start_x + spalte * (button_breite + button_abstand_x), start_y_buttons + zeile * (button_hoehe + button_abstand_y));
lv_obj_add_event_cb(btn, button_event_handler, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_center(label);
}
}
}
// Funktionen für MIDI Note ON und Note OFF Senden
void sendNoteON(byte note) {
Serial.print("MIDI Note ON: ");
Serial.println(note);
MIDI.noteOn(note, 100);
}
void sendNoteOFF(byte note) {
Serial.print("MIDI Note OFF: ");
Serial.println(note);
MIDI.noteOff(note);
}
// Generischer Event Handler für alle Buttons (angepasst für Toggle Buttons und MIDI)
static void button_event_handler(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e); // Button-Objekt bekommen
if (event_code == LV_EVENT_VALUE_CHANGED) { // Geändert zu VALUE_CHANGED für Toggle Buttons
lv_obj_t * label = lv_obj_get_child(obj, NULL); // Label des Buttons holen
uintptr_t midi_note_ptr = (uintptr_t)lv_obj_get_user_data(obj); // Als uintptr_t holen (NEU)
byte midi_note = (byte)midi_note_ptr; // Dann zu byte umwandeln (NEU)
if (label) {
Serial.print("Toggle Button '");
Serial.print(lv_label_get_text(label)); // Button Beschriftung auslesen
Serial.print("' wurde ");
if (lv_obj_has_state(obj, LV_STATE_CHECKED)) {
Serial.println("AUSGEWÄHLT (aktiviert)");
// Sende Note ON, wenn Button ausgewählt (NEU)
sendNoteON(midi_note);
} else {
Serial.println("ABGEWÄHLT (deaktiviert)");
// Sende Note OFF, wenn Button abgewählt (NEU)
sendNoteOFF(midi_note);
}
} else {
Serial.println("Toggle Button (ohne Label) wurde getoggelt"); // Fallback
}
// Hier spezifische Aktionen für jeden Button basierend auf seinem Label oder Objekt-Pointer und Zustand einfügen
}
}
Sketch uses 583752 bytes (44%) of program storage space. Maximum is 1310720 bytes.
Global variables use 107740 bytes (32%) of dynamic memory, leaving 219940 bytes for local variables. Maximum is 327680 bytes.
- Hauptwerk
- Hauptwerk-Konfiguration, Diskussion
- Hauptwerk-Samplesets
- GrandOrgue
- GrandOrgue-Konfiguration, Diskussion
- GrandOrgue-Samplesets
- Sweelinq
- Sweelinq-Konfiguration, Diskussion
- Sweelinq-Samplesets
- Sonstige Orgelsoftware
- Organteq
- Sonstige Sampler
- Hardware
- Spieltische und Selbstbau
- Zubehör (PCs, Monitore, Interfaces etc.)
- Klangabstrahlung
- Musikalisches
- Noten, Einspielungen, Konzerte
- Sonstige Musikthemen
Jetzt anmelden!
Jetzt registrieren!