From f7a2a3840aa8f21f3a4e90893704ac05f22cf650 Mon Sep 17 00:00:00 2001 From: ttbombadil Date: Sun, 18 Oct 2026 09:48:39 +0200 Subject: [PATCH] feat(examples): rebuild the catalog along the course weeks Replace the 35 examples with 51 sketches that follow the IT01-IT08 and IT11 course documents. Each sketch names the slides it illustrates and contains a learning objective, an observable result, experiments, learning questions and Tutor focus questions. New topics include choosing a data type, char/ASCII, global/local scope, blinking, parseInt, #define, time differences, button edges, else-if, switch menus, timing without delay, binary output, arrays as parameters, typedef, a header with struct and functions, test cases and the matrix steps of IT11 with mat_func.h. Fix the Tutor topics: misconceptions now state the wrong belief, and the long-values concept IDs match their content. Point the quality cases to the new example IDs and set the manifest ref to v3.0.0. Co-Authored-By: Claude Opus 5.5 --- README.md | 45 ++- examples/IT01/it01-01-variable-definieren.ino | 82 +++++ examples/IT01/it01-01-variable-speichern.ino | 61 ---- examples/IT01/it01-02-konstante.ino | 50 ++- examples/IT01/it01-03-datentyp-waehlen.ino | 73 +++++ examples/IT01/it01-04-zeichen-ascii.ino | 81 +++++ examples/IT01/it01-05-global-lokal.ino | 72 +++++ .../IT02/it02-01-arithmetische-operatoren.ino | 38 ++- .../IT02/it02-02-vergleichsoperatoren.ino | 33 +- .../IT02/it02-03-boolesche-operatoren.ino | 58 ++-- examples/IT02/it02-04-bitoperatoren.ino | 64 ---- .../IT02/it02-04-wertebereich-ueberlauf.ino | 66 ++++ examples/IT02/it02-05-bitoperatoren.ino | 73 +++++ examples/IT03/it03-01-serielle-ausgabe.ino | 53 +++- .../it03-02-serielles-zeichen-einlesen.ino | 57 ---- examples/IT03/it03-02-zeichen-einlesen.ino | 72 +++++ examples/IT03/it03-03-digitaler-ausgang.ino | 57 ---- examples/IT03/it03-03-zahl-einlesen.ino | 68 ++++ examples/IT03/it03-04-digitaler-eingang.ino | 59 ---- examples/IT03/it03-04-led-blinken.ino | 69 ++++ examples/IT03/it03-05-millis.ino | 57 ---- examples/IT03/it03-05-pins-als-konstanten.ino | 77 +++++ examples/IT03/it03-06-digitaler-eingang.ino | 65 ++++ examples/IT03/it03-07-zeit-messen.ino | 73 +++++ examples/IT03/it03-08-map.ino | 75 +++++ examples/IT04/it04-01-analoger-eingang.ino | 35 +- examples/IT04/it04-02-messwert-in-volt.ino | 62 ---- examples/IT04/it04-02-spannung-berechnen.ino | 69 ++++ examples/IT04/it04-03-pwm-ausgang.ino | 48 +-- examples/IT04/it04-04-map.ino | 57 ---- examples/IT04/it04-04-taster-flanke.ino | 73 +++++ examples/IT04/it04-05-helligkeit-steuern.ino | 71 +++++ examples/IT05/it05-01-if.ino | 57 ++-- examples/IT05/it05-02-if-else.ino | 61 ++-- examples/IT05/it05-03-else-if.ino | 74 +++++ examples/IT05/it05-03-switch.ino | 67 ---- examples/IT05/it05-04-switch-menue.ino | 86 +++++ .../IT05/it05-05-intervall-ohne-delay.ino | 75 +++++ examples/IT06/it06-01-while.ino | 64 ++-- examples/IT06/it06-02-do-while.ino | 54 ++-- examples/IT06/it06-03-for.ino | 62 ++-- examples/IT06/it06-04-binaerzahl.ino | 70 ++++ .../IT06/it06-04-verschachtelte-schleifen.ino | 58 ---- .../IT06/it06-05-verschachtelte-schleifen.ino | 70 ++++ .../IT07/it07-01-funktion-ohne-rueckgabe.ino | 89 ++++-- .../IT07/it07-02-funktion-mit-rueckgabe.ino | 65 ++-- examples/IT07/it07-03-call-by-value.ino | 59 ++-- examples/IT07/it07-04-call-by-reference.ino | 44 ++- .../IT07/it07-05-eindimensionales-array.ino | 68 ++-- .../IT07/it07-06-zweidimensionales-array.ino | 65 ++-- examples/IT07/it07-07-array-an-funktion.ino | 91 ++++++ examples/IT08/it08-01-struct.ino | 58 ++-- examples/IT08/it08-02-array-aus-structs.ino | 67 ---- examples/IT08/it08-02-struct-uebergeben.ino | 88 +++++ examples/IT08/it08-03-array-aus-structs.ino | 94 ++++++ .../IT08/it08-03-struct-als-parameter.ino | 62 ---- examples/IT08/it08-04-funktion-auslagern.ino | 52 --- examples/IT08/it08-04-typedef.ino | 98 ++++++ examples/IT08/it08-05-headerdatei.ino | 73 +++++ examples/IT08/it08-06-funktion-testen.ino | 85 +++++ examples/IT08/messung.h | 24 ++ examples/IT08/rechnen.h | 5 - examples/IT11/it11-01-matrix-ausgeben.ino | 84 +++-- .../IT11/it11-02-matrix-transponieren.ino | 71 ----- .../IT11/it11-02-skalarmultiplikation.ino | 79 +++++ examples/IT11/it11-03-matrizen-addieren.ino | 76 +++-- .../IT11/it11-04-matrix-transponieren.ino | 79 +++++ .../IT11/it11-05-matrizen-multiplizieren.ino | 121 +++++++ examples/IT11/mat_func.h | 22 ++ manifest.json | 300 +++++++++++++----- tutor/manifest.yaml | 4 +- tutor/quality-cases.yaml | 4 +- tutor/topics/long-values.yaml | 26 +- tutor/topics/variables-and-serial.yaml | 4 +- 74 files changed, 3442 insertions(+), 1406 deletions(-) create mode 100644 examples/IT01/it01-01-variable-definieren.ino delete mode 100644 examples/IT01/it01-01-variable-speichern.ino create mode 100644 examples/IT01/it01-03-datentyp-waehlen.ino create mode 100644 examples/IT01/it01-04-zeichen-ascii.ino create mode 100644 examples/IT01/it01-05-global-lokal.ino delete mode 100644 examples/IT02/it02-04-bitoperatoren.ino create mode 100644 examples/IT02/it02-04-wertebereich-ueberlauf.ino create mode 100644 examples/IT02/it02-05-bitoperatoren.ino delete mode 100644 examples/IT03/it03-02-serielles-zeichen-einlesen.ino create mode 100644 examples/IT03/it03-02-zeichen-einlesen.ino delete mode 100644 examples/IT03/it03-03-digitaler-ausgang.ino create mode 100644 examples/IT03/it03-03-zahl-einlesen.ino delete mode 100644 examples/IT03/it03-04-digitaler-eingang.ino create mode 100644 examples/IT03/it03-04-led-blinken.ino delete mode 100644 examples/IT03/it03-05-millis.ino create mode 100644 examples/IT03/it03-05-pins-als-konstanten.ino create mode 100644 examples/IT03/it03-06-digitaler-eingang.ino create mode 100644 examples/IT03/it03-07-zeit-messen.ino create mode 100644 examples/IT03/it03-08-map.ino delete mode 100644 examples/IT04/it04-02-messwert-in-volt.ino create mode 100644 examples/IT04/it04-02-spannung-berechnen.ino delete mode 100644 examples/IT04/it04-04-map.ino create mode 100644 examples/IT04/it04-04-taster-flanke.ino create mode 100644 examples/IT04/it04-05-helligkeit-steuern.ino create mode 100644 examples/IT05/it05-03-else-if.ino delete mode 100644 examples/IT05/it05-03-switch.ino create mode 100644 examples/IT05/it05-04-switch-menue.ino create mode 100644 examples/IT05/it05-05-intervall-ohne-delay.ino create mode 100644 examples/IT06/it06-04-binaerzahl.ino delete mode 100644 examples/IT06/it06-04-verschachtelte-schleifen.ino create mode 100644 examples/IT06/it06-05-verschachtelte-schleifen.ino create mode 100644 examples/IT07/it07-07-array-an-funktion.ino delete mode 100644 examples/IT08/it08-02-array-aus-structs.ino create mode 100644 examples/IT08/it08-02-struct-uebergeben.ino create mode 100644 examples/IT08/it08-03-array-aus-structs.ino delete mode 100644 examples/IT08/it08-03-struct-als-parameter.ino delete mode 100644 examples/IT08/it08-04-funktion-auslagern.ino create mode 100644 examples/IT08/it08-04-typedef.ino create mode 100644 examples/IT08/it08-05-headerdatei.ino create mode 100644 examples/IT08/it08-06-funktion-testen.ino create mode 100644 examples/IT08/messung.h delete mode 100644 examples/IT08/rechnen.h delete mode 100644 examples/IT11/it11-02-matrix-transponieren.ino create mode 100644 examples/IT11/it11-02-skalarmultiplikation.ino create mode 100644 examples/IT11/it11-04-matrix-transponieren.ino create mode 100644 examples/IT11/it11-05-matrizen-multiplizieren.ino create mode 100644 examples/IT11/mat_func.h diff --git a/README.md b/README.md index fcb961c..9e77435 100644 --- a/README.md +++ b/README.md @@ -7,25 +7,52 @@ relative to this repository and its immutable release ref. ## Didactic structure The examples follow the weekly `ITxx` course documents used in the first -semester. Each sketch focuses on one concept and contains four parts: +semester; the week number in a file name matches the number of the course +document. Each sketch focuses on one concept from that week and contains five +parts: 1. a learning objective, -2. a directly observable result, -3. small experiments that modify the sketch, and -4. learning questions without included answers. +2. a reference to the slides it illustrates (`Bezug zu den Unterlagen`), +3. a directly observable result, +4. small experiments that modify the sketch, and +5. learning questions without included answers. + +The sketches use the conventions of the slides: constants in upper case, +`const byte` for pin numbers, `struct Messung` with the `struct` keyword, and +the voltage conversion `messwert / 1024.0 * 5.0`. Comments are German with +umlauts; string literals stay ASCII. Every example compiles for the Arduino Uno +without warnings. File names use the pattern `it--.ino`, for example -`it03-03-digitaler-ausgang.ino`. File names stay ASCII-only for reliable use in +`it03-04-led-blinken.ino`. File names stay ASCII-only for reliable use in URLs and different file systems; German characters may be used in the titles inside `manifest.json`. -The examples are intentionally small reference programs. Complete solutions -for the larger Morse, LED dice, traffic-light, and workpiece-detection exercises -are not part of the public catalog. +Headers are plain `.h` files with definitions, as in the course, so beginners +only ever handle one extra file. `examples/IT11/mat_func.h` is shared by the +matrix examples IT11.2 to IT11.5. + +The weeks IT09 and IT10 are project weeks and have no examples. The examples +are intentionally small reference programs. Complete solutions for the larger +Morse, LED dice, traffic-light, and workpiece-detection exercises are not part +of the public catalog. + +IT08.4 (`typedef` in function signatures) and the IT11 examples (array +parameters sized by constants, default arguments) need an UnoSim version whose +generated function prototypes follow the Arduino IDE, that is, placed before the +first function definition and without default arguments. IT03.6 and IT04.4 +expect an open `INPUT_PULLUP` pin to read `HIGH`, which also needs that version. + +UnoSim does not run the sketch as Uno machine code, so a few observations +differ from the hardware: `int` is 32 bits wide, `double` 64 bits, and a float +printed without a precision shows `2.5` instead of `2.50`. The examples avoid +these cases (overflow is shown with `byte`, floats are always printed with a +precision). Keep it that way in new examples; the UnoSim user guide lists all +differences in its section "Grenzen der Simulation". ## Releases -Releases use immutable tags such as `v2.0.0`; production UnoSim deployments may +Releases use immutable tags such as `v3.0.0`; production UnoSim deployments may pin the corresponding tag or commit SHA. ## Course Content and Tutor authoring diff --git a/examples/IT01/it01-01-variable-definieren.ino b/examples/IT01/it01-01-variable-definieren.ino new file mode 100644 index 0000000..6e94e1e --- /dev/null +++ b/examples/IT01/it01-01-variable-definieren.ino @@ -0,0 +1,82 @@ +/* + * IT01.1 – Eine Variable deklarieren, initialisieren und zuweisen + * + * Lernziel: + * Deklaration, Initialisierung und Zuweisung unterscheiden und erklären, + * was bei einer neuen Zuweisung mit dem alten Wert geschieht. + * + * Bezug zu den Unterlagen: + * IT01 Daten, Folien „Variable deklarieren“ und „Datentypen für ganze Zahlen“ + * (Deklaration + Initialisierung = Definition). + * + * Beobachtung: + * Der serielle Monitor zeigt „Teile: 3“, dann „Teile: 5“ und „Fehler: 0“. + * Serial wird hier nur benutzt, um die Werte sichtbar zu machen. + */ + +int anzahlTeile; // Deklaration: Es gibt jetzt eine Variable vom Typ int +int anzahlFehler = 0; // Definition: Deklaration und Initialisierung in einem Schritt + +void setup() { + Serial.begin(115200); + + anzahlTeile = 3; // Zuweisung + Serial.print("Teile: "); + Serial.println(anzahlTeile); + + anzahlTeile = 5; // Die neue Zuweisung überschreibt den alten Wert + Serial.print("Teile: "); + Serial.println(anzahlTeile); + + Serial.print("Fehler: "); + Serial.println(anzahlFehler); +} + +void loop() { +} + +/* + * Experimente: + * 1. Weise anzahlFehler in setup() den Wert 2 zu und gib ihn erneut aus. + * 2. Deklariere drei weitere Variablen in einer Zeile: int x, y, z; + * 3. Schreibe in einer der Ausgaben AnzahlTeile mit großem A und lies die + * Fehlermeldung des Compilers. + * + * Lernfragen: + * 1. Welche Zeile ist nur eine Deklaration und welche eine Definition? + * 2. Was geschieht mit dem alten Wert, wenn anzahlTeile = 5 ausgeführt wird? + * 3. Warum erkennt der Compiler AnzahlTeile nicht als dieselbe Variable? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen Deklaration, Initialisierung und Zuweisung einer Variablen unterscheiden können." + - "Sie sollen erklären können, dass eine Zuweisung den alten Wert überschreibt." +afterFocus: free +focus: + - id: begriffe + title: "Deklaration, Definition, Zuweisung" + objective: "Die drei Begriffe an den Zeilen des Sketches zeigen und unterscheiden." + questions: + - kind: recall + text: "Welche Zeile ist nur eine Deklaration, und welche Zeile ist eine Definition?" + - kind: concept + text: "Worin unterscheidet sich die Zeile mit `anzahlFehler` von der Zeile, in der `anzahlTeile` deklariert wird?" + - id: ueberschreiben + title: "Einen Wert überschreiben" + objective: "Vorhersagen, welchen Wert eine Variable nach mehreren Zuweisungen besitzt." + questions: + - kind: prediction + text: "Was geschieht mit dem alten Wert von `anzahlTeile`, wenn `anzahlTeile = 5` ausgeführt wird?" + - kind: application + text: "Wie viele Werte kann `anzahlTeile` zu einem Zeitpunkt gleichzeitig speichern?" + - id: namen + title: "Variablennamen" + objective: "Die Regeln für Variablennamen anwenden." + questions: + - kind: concept + text: "Warum meldet der Compiler einen Fehler, wenn `anzahlTeile` an einer Stelle mit großem A geschrieben wird?" + - kind: transfer + text: "Welche weitere Variable würdest du für ein Zählprogramm definieren, und mit welchem Startwert?" +@end-unosim-tutor */ diff --git a/examples/IT01/it01-01-variable-speichern.ino b/examples/IT01/it01-01-variable-speichern.ino deleted file mode 100644 index 5215527..0000000 --- a/examples/IT01/it01-01-variable-speichern.ino +++ /dev/null @@ -1,61 +0,0 @@ -/* - * IT01.1 - Einen Wert in einer Variablen speichern - * - * Lernziel: - * Eine Variable deklarieren, ihr einen Wert zuweisen und den Wert veraendern. - * - * Beobachtung: - * Der serielle Monitor zeigt zuerst 3 und danach 5. - * Serial wird hier nur benutzt, um die Werte sichtbar zu machen. - */ - -int anzahlTeile = 3; - -void setup() { - Serial.begin(115200); - - Serial.println(anzahlTeile); - anzahlTeile = 5; - Serial.println(anzahlTeile); -} - -void loop() { -} - -/* - * Experimente: - * 1. Weise der Variablen vor der zweiten Ausgabe den Wert 12 zu. - * 2. Fuege eine dritte Zuweisung und Ausgabe hinzu. - * - * Lernfragen: - * 1. Welchen Wert besitzt anzahlTeile direkt nach der Deklaration? - * 2. Was geschieht mit dem alten Wert bei einer neuen Zuweisung? - * 3. Warum erscheint jede Ausgabe nur einmal? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen erklären können, welche Rolle die Variable im aktuellen Sketch spielt." - - "Sie sollen den Zusammenhang zwischen gespeichertem Wert und serieller Ausgabe verstehen." -afterFocus: free -focus: - - id: wert-und-zuweisung - title: "Wert und Zuweisung" - objective: "Erklären, welchen Wert die Variable nach Deklaration und nach einer neuen Zuweisung besitzt und was mit dem alten Wert geschieht." - questions: - - kind: recall - text: "Welchen Wert besitzt `anzahlTeile` direkt nach der Deklaration?" - - kind: prediction - text: "Was geschieht mit dem alten Wert von `anzahlTeile`, wenn `anzahlTeile = 5` ausgeführt wird?" - - kind: application - text: "Wie viele Werte kann `anzahlTeile` zu einem Zeitpunkt gleichzeitig speichern?" - - id: sichtbare-ausgabe - title: "Sichtbare Ausgabe" - objective: "Begründen, warum die Ausgaben genau so und nur einmal erscheinen, und eine Erweiterung planen." - questions: - - kind: concept - text: "Warum erscheint jede Ausgabe von `Serial.println` nur einmal?" - - kind: transfer - text: "Welche weitere Zuweisung und Ausgabe würdest du ergänzen, um einen dritten Wert sichtbar zu machen?" -@end-unosim-tutor */ diff --git a/examples/IT01/it01-02-konstante.ino b/examples/IT01/it01-02-konstante.ino index 4b4d238..2b50f0c 100644 --- a/examples/IT01/it01-02-konstante.ino +++ b/examples/IT01/it01-02-konstante.ino @@ -1,20 +1,36 @@ /* - * IT01.2 - Einen unveraenderlichen Wert festlegen + * IT01.2 – Einen unveränderlichen Wert festlegen * * Lernziel: - * Einen Wert mit const als Konstante deklarieren. + * Werte, die sich nie ändern sollen, mit const als Konstante definieren und + * die vordefinierten Arduino-Konstanten kennenlernen. + * + * Bezug zu den Unterlagen: + * IT01 Daten, Folien „Konstanten“ und „Festlegungen“ (Konstanten werden + * immer großgeschrieben). * * Beobachtung: - * Der serielle Monitor zeigt den festgelegten Grenzwert. - * Serial wird hier nur benutzt, um den Wert sichtbar zu machen. + * Der serielle Monitor zeigt die eigene Konstante, einen Umrechnungsfaktor + * und die Zahlenwerte der vordefinierten Konstanten HIGH und LOW. */ const byte MAXIMALE_ANZAHL = 10; +const float ZOLL_IN_CM = 2.54; void setup() { Serial.begin(115200); + Serial.print("Maximale Anzahl: "); Serial.println(MAXIMALE_ANZAHL); + + Serial.print("1 Zoll = "); + Serial.print(ZOLL_IN_CM, 2); + Serial.println(" cm"); + + Serial.print("HIGH hat den Wert "); + Serial.println(HIGH); + Serial.print("LOW hat den Wert "); + Serial.println(LOW); } void loop() { @@ -22,13 +38,15 @@ void loop() { /* * Experimente: - * 1. Aendere den Wert der Konstante auf 20. - * 2. Versuche nach der Deklaration MAXIMALE_ANZAHL = 5; zu ergaenzen. + * 1. Ändere den Wert von MAXIMALE_ANZAHL auf 20. + * 2. Ergänze in setup() die Zeile MAXIMALE_ANZAHL = 5; und lies die + * Fehlermeldung des Compilers. + * 3. Gib zusätzlich die vordefinierte Konstante A0 aus. * * Lernfragen: * 1. Wodurch unterscheidet sich eine Konstante von einer Variablen? * 2. Warum meldet der Compiler beim zweiten Experiment einen Fehler? - * 3. Warum werden Konstantennamen hier grossgeschrieben? + * 3. Warum werden Konstantennamen großgeschrieben? */ /* @unosim-tutor @@ -40,20 +58,20 @@ afterFocus: free focus: - id: konstante-oder-variable title: "Konstante oder Variable" - objective: "Den Unterschied zwischen einer Konstante und einer Variablen erklären und die Folge einer Änderung vorhersagen." + objective: "Den Unterschied zwischen Konstante und Variable erklären und die Folge einer Änderung vorhersagen." questions: - kind: concept text: "Wodurch unterscheidet sich `MAXIMALE_ANZAHL` von einer gewöhnlichen Variablen?" - kind: prediction - text: "Was würde der Compiler melden, wenn man später versucht, `MAXIMALE_ANZAHL` einen neuen Wert zuzuweisen, und warum?" - - id: const-und-namen - title: "const und Namenswahl" - objective: "Die Rolle von `const` und die Namenskonvention für Konstanten begründen." + text: "Was meldet der Compiler, wenn in `setup` versucht wird, `MAXIMALE_ANZAHL` einen neuen Wert zuzuweisen?" + - id: konventionen + title: "Namen und vordefinierte Konstanten" + objective: "Die Namenskonvention begründen und vordefinierte Konstanten einordnen." questions: - kind: concept - text: "Welche Aufgabe hat das Schlüsselwort `const` in der Deklaration?" - - kind: concept - text: "Warum ist der Name `MAXIMALE_ANZAHL` hier großgeschrieben?" + text: "Warum sind die Namen `MAXIMALE_ANZAHL` und `ZOLL_IN_CM` großgeschrieben?" + - kind: recall + text: "Welche Zahlenwerte stehen hinter den vordefinierten Konstanten `HIGH` und `LOW`?" - kind: transfer - text: "Für welchen weiteren Wert in einem Sketch wäre eine Konstante sinnvoll?" + text: "Für welchen weiteren Wert in einem Arduino-Programm wäre eine Konstante sinnvoll?" @end-unosim-tutor */ diff --git a/examples/IT01/it01-03-datentyp-waehlen.ino b/examples/IT01/it01-03-datentyp-waehlen.ino new file mode 100644 index 0000000..9e439e5 --- /dev/null +++ b/examples/IT01/it01-03-datentyp-waehlen.ino @@ -0,0 +1,73 @@ +/* + * IT01.3 – Einen Datentyp begründet auswählen + * + * Lernziel: + * Für einen bekannten Wertebereich einen passenden Datentyp auswählen und + * die Auswahl über die Anzahl der Bitmuster begründen. + * + * Bezug zu den Unterlagen: + * IT01 Daten, Folie „Argumentation für die Auswahl von Datentypen“ + * (Wert zwischen 0 und 1023). + * + * Beobachtung: + * Derselbe Wert 1023 wird einmal als unsigned int und einmal als byte + * gespeichert. Der byte-Wert ist 255 – der Wert passt nicht hinein. + */ + +// Anforderung: Ein Sensorwert liegt immer zwischen 0 und 1023. +unsigned int sensorwertPassend = 1023; + +void setup() { + Serial.begin(115200); + + byte sensorwertZuKlein = sensorwertPassend; + + Serial.print("Als unsigned int gespeichert: "); + Serial.println(sensorwertPassend); + Serial.print("Als byte gespeichert: "); + Serial.println(sensorwertZuKlein); +} + +void loop() { +} + +/* + * Experimente: + * 1. Setze sensorwertPassend auf 255 und vergleiche beide Ausgaben. + * 2. Setze sensorwertPassend auf 300. Welcher byte-Wert erscheint? + * 3. Begründe schriftlich nach dem Schema der Folie, welchen Datentyp du für + * einen Füllstand von 0,4 m bis 6,0 m wählen würdest. + * + * Lernfragen: + * 1. Wie viele verschiedene Bitmuster besitzt ein 8-Bit-Datentyp, wie viele + * ein 16-Bit-Datentyp? + * 2. Warum ist byte für den Bereich 0 bis 1023 nicht geeignet? + * 3. Warum darf hier das Schlüsselwort unsigned verwendet werden? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen Datentyp für einen gegebenen Wertebereich auswählen und begründen können." + - "Sie sollen die Anzahl der Bitmuster eines Datentyps aus seiner Bitbreite bestimmen können." +afterFocus: free +focus: + - id: bitmuster + title: "Bitmuster und Wertebereich" + objective: "Aus der Bitbreite die Anzahl der Bitmuster und den größten Wert bestimmen." + questions: + - kind: recall + text: "Wie viele verschiedene Bitmuster kann ein 8-Bit-Datentyp wie `byte` darstellen?" + - kind: concept + text: "Warum ist `byte` für Werte bis 1023 zu klein?" + - id: argumentation + title: "Den Datentyp begründen" + objective: "Die Wahl von `unsigned int` nach dem Schema der Folie begründen." + questions: + - kind: concept + text: "Warum darf `sensorwertPassend` mit `unsigned` deklariert werden?" + - kind: prediction + text: "Welcher Wert erscheint für `sensorwertZuKlein`, wenn `sensorwertPassend` den Wert 300 hat?" + - kind: transfer + text: "Welchen Datentyp würdest du für einen Füllstand von 0,4 m bis 6,0 m wählen, und wie begründest du das?" +@end-unosim-tutor */ diff --git a/examples/IT01/it01-04-zeichen-ascii.ino b/examples/IT01/it01-04-zeichen-ascii.ino new file mode 100644 index 0000000..c127d77 --- /dev/null +++ b/examples/IT01/it01-04-zeichen-ascii.ino @@ -0,0 +1,81 @@ +/* + * IT01.4 – Zeichen und ihre Codes + * + * Lernziel: + * Erklären, dass eine char-Variable ein Zeichen als Zahl aus der + * ASCII-Tabelle speichert. + * + * Bezug zu den Unterlagen: + * IT01 Daten, Folie „Datentyp für Zeichen“; IT02, Folie „Datentyp char verstehen“. + * + * Beobachtung: + * A hat den Code 65, der Code 66 ergibt das Zeichen B, # hat den Code 35 + * und auf A folgt B. + */ + +char zeichen1 = 'A'; +char zeichen2 = 66; // Zahlenwert aus der ASCII-Tabelle +char zeichen3 = '#'; + +void setup() { + Serial.begin(115200); + + int code1 = zeichen1; + Serial.print(zeichen1); + Serial.print(" hat den Code "); + Serial.println(code1); + + Serial.print("Code 66 ist das Zeichen "); + Serial.println(zeichen2); + + int code3 = zeichen3; + Serial.print(zeichen3); + Serial.print(" hat den Code "); + Serial.println(code3); + + char naechstes = zeichen1 + 1; + Serial.print("Auf "); + Serial.print(zeichen1); + Serial.print(" folgt "); + Serial.println(naechstes); +} + +void loop() { +} + +/* + * Experimente: + * 1. Setze zeichen1 auf 'a'. Um wie viel unterscheiden sich die Codes von + * Groß- und Kleinbuchstaben? + * 2. Gib mit Zahlenwerten aus der ASCII-Tabelle die Buchstaben X, Y und Z aus. + * 3. Setze zeichen2 auf 48. Welches Zeichen erscheint? + * + * Lernfragen: + * 1. Was wird in einer char-Variablen tatsächlich gespeichert? + * 2. Warum ergibt zeichen1 + 1 den nächsten Buchstaben? + * 3. Warum ist das Zeichen '7' nicht dasselbe wie die Zahl 7? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen erklären können, dass `char` ein Zeichen als Zahlencode speichert." +afterFocus: free +focus: + - id: zeichen-und-code + title: "Zeichen und Code" + objective: "Den Zusammenhang zwischen einem Zeichen und seinem ASCII-Code erklären." + questions: + - kind: concept + text: "Wie hängen `zeichen1` und `code1` zusammen?" + - kind: recall + text: "Welches Zeichen gehört zum Code, mit dem `zeichen2` initialisiert wird?" + - id: mit-zeichen-rechnen + title: "Mit Zeichen rechnen" + objective: "Vorhersagen, welches Zeichen eine Rechnung mit Codes ergibt." + questions: + - kind: prediction + text: "Welches Zeichen wird für `naechstes` ausgegeben, wenn `zeichen1` mit dem Kleinbuchstaben a initialisiert wird?" + - kind: transfer + text: "Wie würdest du aus einem Großbuchstaben in `zeichen1` den passenden Kleinbuchstaben berechnen?" +@end-unosim-tutor */ diff --git a/examples/IT01/it01-05-global-lokal.ino b/examples/IT01/it01-05-global-lokal.ino new file mode 100644 index 0000000..3a32629 --- /dev/null +++ b/examples/IT01/it01-05-global-lokal.ino @@ -0,0 +1,72 @@ +/* + * IT01.5 – Globale und lokale Variablen + * + * Lernziel: + * Den Gültigkeitsbereich globaler und lokaler Variablen unterscheiden. + * + * Bezug zu den Unterlagen: + * IT01 Daten, Folie „Globale Variablen / Lokale Variablen“. + * + * Beobachtung: + * setup() setzt den globalen Zähler auf 10. Danach erhöht loop() ihn jede + * Sekunde um 1: 11, 12, 13, ... + * delay(1000) wartet eine Sekunde (Vorgriff auf IT03). + */ + +int zaehlerGlobal = 0; // global: überall im Programm verwendbar + +void setup() { + Serial.begin(115200); + + int startwert = 10; // lokal: nur in setup() verwendbar + zaehlerGlobal = startwert; + Serial.print("setup: zaehlerGlobal = "); + Serial.println(zaehlerGlobal); +} + +void loop() { + int schritt = 1; // lokal: wird bei jedem Durchlauf von loop() neu angelegt + zaehlerGlobal = zaehlerGlobal + schritt; + Serial.print("loop: zaehlerGlobal = "); + Serial.println(zaehlerGlobal); + delay(1000); +} + +/* + * Experimente: + * 1. Gib in loop() zusätzlich startwert aus und lies die Fehlermeldung. + * 2. Ergänze in loop() eine lokale Variable int zaehlerLokal = 0;, erhöhe sie + * ebenfalls um schritt und gib sie aus. Vergleiche mit zaehlerGlobal. + * 3. Setze schritt auf 5. + * + * Lernfragen: + * 1. Wo darf zaehlerGlobal verwendet werden, wo startwert? + * 2. Warum behält zaehlerGlobal seinen Wert zwischen zwei Durchläufen von loop()? + * 3. Warum bleibt zaehlerLokal im zweiten Experiment immer bei 1? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen den Gültigkeitsbereich globaler und lokaler Variablen unterscheiden können." +afterFocus: free +focus: + - id: gueltigkeit + title: "Gültigkeitsbereich" + objective: "Bestimmen, in welchen Funktionen eine Variable verwendet werden darf." + questions: + - kind: concept + text: "Warum kann `startwert` in `loop` nicht verwendet werden?" + - kind: recall + text: "In welchen Funktionen darf `zaehlerGlobal` verwendet werden?" + - id: lebensdauer + title: "Werte über Durchläufe hinweg" + objective: "Erklären, welche Variable ihren Wert zwischen zwei Durchläufen von `loop` behält." + questions: + - kind: prediction + text: "Welche Werte von `zaehlerGlobal` erscheinen in den ersten drei Durchläufen von `loop`?" + - kind: concept + text: "Warum beginnt `schritt` bei jedem Durchlauf von `loop` wieder mit demselben Wert?" + - kind: transfer + text: "Wo würdest du eine Variable deklarieren, die zählen soll, wie oft `loop` durchlaufen wurde?" +@end-unosim-tutor */ diff --git a/examples/IT02/it02-01-arithmetische-operatoren.ino b/examples/IT02/it02-01-arithmetische-operatoren.ino index 7b2da4b..87fa3c6 100644 --- a/examples/IT02/it02-01-arithmetische-operatoren.ino +++ b/examples/IT02/it02-01-arithmetische-operatoren.ino @@ -1,15 +1,22 @@ /* - * IT02.1 - Mit arithmetischen Operatoren rechnen + * IT02.1 – Mit arithmetischen Operatoren rechnen * * Lernziel: - * Die Operatoren +, -, *, / und % anwenden. + * Die Operatoren +, -, *, / und % anwenden und den Unterschied zwischen + * ganzzahliger Division und Division mit Kommazahlen erklären. + * + * Bezug zu den Unterlagen: + * IT02 Datenmanipulation, Folien „Addition / Subtraktion / Multiplikation / + * Division“ und „Modulo“ (13 / 5 = 2 Rest 3). * * Beobachtung: - * Der serielle Monitor zeigt die Ergebnisse derselben zwei Operanden. + * a / b ergibt 2, a % b ergibt 3. Mit float-Variablen ergibt x / y = 2.60. */ int a = 13; int b = 5; +float x = 13.0; +float y = 5.0; void setup() { Serial.begin(115200); @@ -24,6 +31,9 @@ void setup() { Serial.println(a / b); Serial.print("a % b = "); Serial.println(a % b); + + Serial.print("x / y = "); + Serial.println(x / y, 2); } void loop() { @@ -31,32 +41,32 @@ void loop() { /* * Experimente: - * 1. Setze a auf 20 und b auf 4. - * 2. Deklariere a und b als float und entferne die Modulo-Ausgabe. + * 1. Setze a auf 20 und b auf 4. Welchen Rest liefert a % b jetzt? + * 2. Ergänze float z = a / b; und gib z mit zwei Nachkommastellen aus. + * Warum erscheint 2.00 und nicht 2.60? + * 3. Ergänze a = a + 1; vor den Ausgaben. Schreibe dasselbe danach mit a++; * * Lernfragen: * 1. Warum ist 13 / 5 bei Variablen vom Typ int gleich 2? * 2. Welche Bedeutung hat das Ergebnis von 13 % 5? - * 3. Bei welchem Experiment darf der Operator % nicht verwendet werden? + * 3. Wann wird bei einer Rechnung mit Nachkommastellen gerechnet? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - "Die Studierenden sollen die Operatoren +, -, *, / und % anwenden können." - - "Sie sollen das Verhalten der Ganzzahldivision erklären können." + - "Sie sollen erklären können, wann ganzzahlig und wann mit Nachkommastellen dividiert wird." afterFocus: free focus: - id: ganzzahldivision - title: "Ganzzahldivision" - objective: "Erklären, warum die Division zweier `int`-Werte ein ganzzahliges Ergebnis liefert." + title: "Ganzzahlige Division" + objective: "Erklären, warum `a / b` und `x / y` verschiedene Ergebnisse liefern." questions: - kind: concept - text: "Warum liefert `a / b` bei Variablen vom Typ `int` ein ganzzahliges Ergebnis?" + text: "Warum liefert `a / b` ein anderes Ergebnis als `x / y`?" - kind: prediction - text: "Welches Ergebnis erwartest du für `a / b`, wenn `a` den Wert 13 und `b` den Wert 5 hat?" - - kind: transfer - text: "Was müsstest du an den Deklarationen ändern, um ein Ergebnis mit Nachkommastellen zu erhalten?" + text: "Welches Ergebnis erwartest du, wenn das Ergebnis von `a / b` in einer zusätzlichen Variablen vom Typ `float` gespeichert wird?" - id: modulo title: "Rest einer Division" objective: "Die Bedeutung des Rests erklären und einen Einsatzzweck nennen." @@ -64,5 +74,5 @@ focus: - kind: concept text: "Welche Bedeutung hat das Ergebnis von `a % b`?" - kind: application - text: "Wofür könnte man den Rest einer Division in einem Programm nutzen?" + text: "Wie könntest du mit dem Operator `%` prüfen, ob eine Zahl gerade ist?" @end-unosim-tutor */ diff --git a/examples/IT02/it02-02-vergleichsoperatoren.ino b/examples/IT02/it02-02-vergleichsoperatoren.ino index d9502d4..c22c1bd 100644 --- a/examples/IT02/it02-02-vergleichsoperatoren.ino +++ b/examples/IT02/it02-02-vergleichsoperatoren.ino @@ -1,11 +1,16 @@ /* - * IT02.2 - Zwei Werte vergleichen + * IT02.2 – Zwei Werte vergleichen * * Lernziel: - * Vergleichsoperatoren anwenden und ihr boolesches Ergebnis beobachten. + * Vergleichsoperatoren anwenden, ihr Ergebnis als Wahrheitswert deuten und + * den Vergleich == von der Zuweisung = unterscheiden. + * + * Bezug zu den Unterlagen: + * IT02 Datenmanipulation, Folien „Vergleichsoperatoren“ und + * „Zuweisungs- und Vergleichsoperator“ (sehr häufige Fehlerursache). * * Beobachtung: - * Ein wahres Ergebnis erscheint als 1, ein falsches als 0. + * Ein wahrer Vergleich erscheint als 1, ein falscher als 0. */ int a = 7; @@ -22,6 +27,10 @@ void setup() { Serial.println(a < b); Serial.print("a >= b: "); Serial.println(a >= b); + + bool gleich = (a == b); + Serial.print("gleich: "); + Serial.println(gleich); } void loop() { @@ -29,20 +38,22 @@ void loop() { /* * Experimente: - * 1. Setze a ebenfalls auf 10. - * 2. Ersetze in einer Ausgabe == durch = und beobachte das Ergebnis. + * 1. Setze a ebenfalls auf 10. Welche Ergebnisse ändern sich? + * 2. Ersetze in der Zeile mit gleich das == durch = und gib danach auch a aus. + * Was ist mit a passiert? + * 3. Ergänze die Vergleiche a > b und a <= b. * * Lernfragen: * 1. Welche Vergleiche sind bei a = 7 und b = 10 wahr? * 2. Was ist der Unterschied zwischen = und ==? - * 3. Welchen Datentyp hat das Ergebnis eines Vergleichs? + * 3. Welche zwei Werte kann das Ergebnis eines Vergleichs annehmen? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - "Die Studierenden sollen Vergleichsoperatoren anwenden und ihr Ergebnis deuten können." - - "Sie sollen Zuweisung und Vergleich unterscheiden können." + - "Sie sollen Zuweisung und Vergleich sicher unterscheiden können." afterFocus: free focus: - id: vergleichsergebnis @@ -55,12 +66,12 @@ focus: text: "Wie werden ein wahres und ein falsches Ergebnis in der Ausgabe dargestellt?" - id: zuweisung-oder-vergleich title: "Zuweisung oder Vergleich" - objective: "Den Unterschied zwischen `=` und `==` erklären und den Ergebnistyp eines Vergleichs nennen." + objective: "Den Unterschied zwischen `=` und `==` erklären und die Folge einer Verwechslung vorhersagen." questions: - kind: concept text: "Was ist der Unterschied zwischen `=` und `==`?" - - kind: concept - text: "Welchen Datentyp hat das Ergebnis eines Vergleichs?" - kind: prediction - text: "Was würde sich ändern, wenn `b` den Wert 7 hätte?" + text: "Welchen Wert hat `a` danach, wenn in der Zeile mit `gleich` versehentlich nur ein Gleichheitszeichen steht?" + - kind: transfer + text: "Wie kannst du beim Programmieren verhindern, dass du `=` und `==` verwechselst?" @end-unosim-tutor */ diff --git a/examples/IT02/it02-03-boolesche-operatoren.ino b/examples/IT02/it02-03-boolesche-operatoren.ino index 617c46e..ff73f1e 100644 --- a/examples/IT02/it02-03-boolesche-operatoren.ino +++ b/examples/IT02/it02-03-boolesche-operatoren.ino @@ -1,25 +1,37 @@ /* - * IT02.3 - Boolesche Aussagen verknuepfen + * IT02.3 – Wahrheitswerte verknüpfen * * Lernziel: - * Die Operatoren &&, || und ! auf Wahrheitswerte anwenden. + * Die Operatoren &&, || und ! auf Wahrheitswerte anwenden und mit + * Beispielen aus der Steuerungstechnik verbinden. + * + * Bezug zu den Unterlagen: + * IT02 Datenmanipulation, Folien „Wahrheitswerte“ und + * „Wahrheitswerte und ihre Verknüpfung“ (Zweihandbedienung, Not-Aus). * * Beobachtung: - * Der serielle Monitor zeigt fuer jede Aussage 1 oder 0. + * Der Antrieb bleibt aus (0), weil nur ein Taster gedrückt ist. + * Die Antriebe werden gestoppt (1), weil ein Not-Aus-Taster gedrückt ist. */ -bool schalter1 = true; -bool schalter2 = false; +bool tasterLinks = true; +bool tasterRechts = false; +bool notAus1 = false; +bool notAus2 = true; void setup() { Serial.begin(115200); - Serial.print("schalter1 UND schalter2: "); - Serial.println(schalter1 && schalter2); - Serial.print("schalter1 ODER schalter2: "); - Serial.println(schalter1 || schalter2); - Serial.print("NICHT schalter1: "); - Serial.println(!schalter1); + bool antriebEin = tasterLinks && tasterRechts; // nur mit beiden Händen + bool antriebeStoppen = notAus1 || notAus2; // ein Not-Aus genügt + bool signallampe = !tasterLinks; // Lampe aus, solange gedrückt + + Serial.print("Antrieb ein: "); + Serial.println(antriebEin); + Serial.print("Antriebe stoppen: "); + Serial.println(antriebeStoppen); + Serial.print("Signallampe: "); + Serial.println(signallampe); } void loop() { @@ -27,35 +39,37 @@ void loop() { /* * Experimente: - * 1. Setze beide Schalter auf true. - * 2. Setze beide Schalter auf false. + * 1. Setze beide Taster auf true. + * 2. Setze beide Not-Aus-Taster auf false. + * 3. Setze tasterLinks = 456; statt true. Welcher Wahrheitswert entsteht? * * Lernfragen: - * 1. Wann ergibt eine UND-Verknuepfung true? - * 2. Wann ergibt eine ODER-Verknuepfung false? - * 3. Welche Wirkung hat der Operator ! auf einen Wahrheitswert? + * 1. Wann ergibt eine UND-Verknüpfung true? + * 2. Wann ergibt eine ODER-Verknüpfung false? + * 3. Welche Zahlenwerte gelten als FALSE, welche als TRUE? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - "Die Studierenden sollen UND, ODER und NICHT auf Wahrheitswerte anwenden können." + - "Sie sollen eine Steuerungsanforderung in eine boolesche Verknüpfung übersetzen können." afterFocus: free focus: - - id: verknuepfungen + - id: und-oder title: "UND und ODER" - objective: "Die Ergebnisse von UND und ODER bei gegebenen Wahrheitswerten bestimmen und begründen." + objective: "Die Ergebnisse von UND und ODER bestimmen und auf die Beispiele beziehen." questions: - kind: concept - text: "Wann ergibt `schalter1 && schalter2` den Wert `true`?" + text: "Warum wird für `antriebEin` der Operator `&&` und nicht `||` verwendet?" - kind: prediction - text: "Welche Ausgabe erwartest du für `schalter1 || schalter2` mit den aktuellen Werten?" + text: "Welchen Wert hat `antriebeStoppen`, wenn beide Not-Aus-Taster `false` sind?" - id: negation title: "Negation" objective: "Die Wirkung von `!` erklären und eine eigene Verknüpfung formulieren." questions: - kind: concept - text: "Welche Wirkung hat der Operator `!` auf `schalter1`?" + text: "Welche Wirkung hat der Operator `!` in der Zeile mit `signallampe`?" - kind: transfer - text: "Wie würdest du ausdrücken, dass genau einer der beiden Schalter `true` ist?" + text: "Wie würdest du ausdrücken, dass der Antrieb nur läuft, wenn beide Taster gedrückt sind und kein Not-Aus betätigt ist?" @end-unosim-tutor */ diff --git a/examples/IT02/it02-04-bitoperatoren.ino b/examples/IT02/it02-04-bitoperatoren.ino deleted file mode 100644 index fca577f..0000000 --- a/examples/IT02/it02-04-bitoperatoren.ino +++ /dev/null @@ -1,64 +0,0 @@ -/* - * IT02.4 - Einzelne Bits untersuchen - * - * Lernziel: - * Eine Bitmaske mit dem bitweisen UND-Operator anwenden. - * - * Beobachtung: - * Die Maske prueft, ob Bit 2 im Ausgangswert gesetzt ist. - */ - -byte wert = 0b00101101; -const byte MASKE_BIT_2 = 0b00000100; - -void setup() { - Serial.begin(115200); - - Serial.print("Wert: "); - Serial.println(wert, BIN); - Serial.print("Maske: "); - Serial.println(MASKE_BIT_2, BIN); - Serial.print("Ergebnis: "); - Serial.println(wert & MASKE_BIT_2, BIN); -} - -void loop() { -} - -/* - * Experimente: - * 1. Setze wert auf 0b00101001. - * 2. Aendere die Maske so, dass Bit 3 geprueft wird. - * 3. Ersetze & durch | und vergleiche die Ausgabe. - * - * Lernfragen: - * 1. Warum bleibt bei UND nur ein gesetztes Bit erhalten? - * 2. Welchen Dezimalwert hat MASKE_BIT_2? - * 3. Worin unterscheiden sich & und &&? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen eine Bitmaske mit dem bitweisen UND anwenden können." -afterFocus: free -focus: - - id: bitmaske - title: "Bitmaske" - objective: "Erklären, wie eine Maske einzelne Bits herausfiltert, und das Ergebnis vorhersagen." - questions: - - kind: concept - text: "Warum bleibt bei `wert & MASKE_BIT_2` nur ein gesetztes Bit erhalten?" - - kind: prediction - text: "Welches Ergebnis erwartest du für `wert & MASKE_BIT_2`, wenn Bit 2 in `wert` nicht gesetzt wäre?" - - id: darstellung-und-abgrenzung - title: "Darstellung und Abgrenzung" - objective: "Die Binärausgabe deuten und bitweise von logischen Operatoren unterscheiden." - questions: - - kind: concept - text: "Welche Rolle spielt `BIN` in `Serial.println(wert, BIN)`?" - - kind: concept - text: "Worin unterscheidet sich der bitweise Operator `&` vom logischen UND mit zwei Und-Zeichen?" - - kind: transfer - text: "Wie würdest du prüfen, ob Bit 3 in `wert` gesetzt ist?" -@end-unosim-tutor */ diff --git a/examples/IT02/it02-04-wertebereich-ueberlauf.ino b/examples/IT02/it02-04-wertebereich-ueberlauf.ino new file mode 100644 index 0000000..3b2c204 --- /dev/null +++ b/examples/IT02/it02-04-wertebereich-ueberlauf.ino @@ -0,0 +1,66 @@ +/* + * IT02.4 – Die Grenze eines Datentyps sichtbar machen + * + * Lernziel: + * Beobachten und erklären, was beim Überschreiten des Wertebereichs eines + * Datentyps geschieht. + * + * Bezug zu den Unterlagen: + * IT02 Datenmanipulation, Abschnitt „Wertebereiche mit Programmen sichtbar + * machen“ (Grenzen eines Datentyps anzeigen). + * + * Beobachtung: + * Der Zähler läuft 250, 251, ..., 255 und springt dann auf 0, 1, 2, ... + * delay(300) wartet 300 ms zwischen zwei Ausgaben (Vorgriff auf IT03). + */ + +byte zaehler = 250; + +void setup() { + Serial.begin(115200); +} + +void loop() { + Serial.println(zaehler); + zaehler = zaehler + 1; + delay(300); +} + +/* + * Experimente: + * 1. Starte zaehler bei 5 und verringere ihn mit zaehler = zaehler - 1;. + * Was folgt auf 0? + * 2. Gib zusätzlich zaehler mit Serial.println(zaehler, BIN); aus und + * beobachte die Bitmuster beim Sprung von 255 auf 0. + * 3. Erhöhe zaehler in jedem Schritt um 10. + * + * Lernfragen: + * 1. Warum folgt auf 255 der Wert 0? + * 2. Welches Bitmuster hat 255 und welches entsteht, wenn 1 addiert wird? + * 3. Welche Folgen kann ein solcher Überlauf in einem Steuerungsprogramm haben? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen erklären können, was beim Überschreiten eines Wertebereichs geschieht." + - "Sie sollen daraus ableiten, warum der Datentyp zum Wertebereich passen muss." +afterFocus: free +focus: + - id: ueberlauf + title: "Überlauf" + objective: "Den Sprung von 255 auf 0 mit dem Bitmuster des Datentyps erklären." + questions: + - kind: concept + text: "Warum folgt bei `zaehler` auf 255 der Wert 0?" + - kind: prediction + text: "Welcher Wert folgt auf 0, wenn `zaehler` in jedem Durchlauf um eins verringert wird?" + - id: folgen + title: "Folgen für Programme" + objective: "Die Bedeutung eines Überlaufs für ein reales Programm einschätzen." + questions: + - kind: application + text: "Was könnte passieren, wenn `zaehler` in einer Anlage die Anzahl produzierter Teile zählt?" + - kind: transfer + text: "Welchen Datentyp würdest du für `zaehler` wählen, wenn bis zu 1000 Teile gezählt werden sollen?" +@end-unosim-tutor */ diff --git a/examples/IT02/it02-05-bitoperatoren.ino b/examples/IT02/it02-05-bitoperatoren.ino new file mode 100644 index 0000000..b7fc2da --- /dev/null +++ b/examples/IT02/it02-05-bitoperatoren.ino @@ -0,0 +1,73 @@ +/* + * IT02.5 – Einzelne Bits prüfen und setzen (optional) + * + * Lernziel: + * Mit dem bitweisen UND ein Bit prüfen (Bitmaske) und mit dem bitweisen + * ODER ein Bit setzen („Aufodern“). + * + * Bezug zu den Unterlagen: + * IT02 Datenmanipulation, Abschnitt „Bitweise Operatoren“ und Folie + * „Der Nutzen von bitweisen Operatoren“. Laut Folie nicht klausurrelevant. + * + * Beobachtung: + * Zuerst ist Bit 2 nicht gesetzt (0). Nach dem Aufodern ist es gesetzt (1). + * BIN gibt keine führenden Nullen aus. + */ + +byte wert = 0b00101001; +const byte MASKE_BIT_2 = 0b00000100; // dezimal 4 + +void setup() { + Serial.begin(115200); + + Serial.print("wert vorher: "); + Serial.println(wert, BIN); + Serial.print("Bit 2 gesetzt? "); + Serial.println((wert & MASKE_BIT_2) && 1); + + wert = wert | MASKE_BIT_2; // Bit 2 setzen + + Serial.print("wert nachher: "); + Serial.println(wert, BIN); + Serial.print("Bit 2 gesetzt? "); + Serial.println((wert & MASKE_BIT_2) && 1); +} + +void loop() { +} + +/* + * Experimente: + * 1. Ändere die Maske so, dass Bit 0 geprüft wird. + * 2. Gib wert & MASKE_BIT_2 einmal ohne && 1 aus. Warum erscheint 100? + * 3. Vergleiche die Ausgaben von wert & 6 und wert && 6. + * + * Lernfragen: + * 1. Warum bleibt bei wert & MASKE_BIT_2 höchstens ein gesetztes Bit übrig? + * 2. Welche Wirkung hat wert | MASKE_BIT_2 auf die übrigen Bits? + * 3. Worin unterscheiden sich & und &&? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen mit einer Bitmaske ein Bit prüfen und setzen können." +afterFocus: free +focus: + - id: bitmaske + title: "Bit prüfen" + objective: "Erklären, wie eine Maske ein einzelnes Bit herausfiltert." + questions: + - kind: concept + text: "Warum bleibt bei `wert & MASKE_BIT_2` höchstens ein gesetztes Bit übrig?" + - kind: concept + text: "Wozu dient `&& 1` am Ende der Prüfung?" + - id: bit-setzen + title: "Bit setzen" + objective: "Die Wirkung des bitweisen ODER beschreiben und vom logischen UND abgrenzen." + questions: + - kind: prediction + text: "Was ändert sich an `wert`, wenn `wert | MASKE_BIT_2` ein zweites Mal ausgeführt wird?" + - kind: transfer + text: "Wie würdest du prüfen, ob Bit 3 in `wert` gesetzt ist?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-01-serielle-ausgabe.ino b/examples/IT03/it03-01-serielle-ausgabe.ino index 27e80a8..f8428c4 100644 --- a/examples/IT03/it03-01-serielle-ausgabe.ino +++ b/examples/IT03/it03-01-serielle-ausgabe.ino @@ -1,19 +1,39 @@ /* - * IT03.1 - Text seriell ausgeben + * IT03.1 – Text und Zahlen seriell ausgeben * * Lernziel: - * Die serielle Verbindung starten und Text mit print() und println() ausgeben. + * Die serielle Verbindung starten und Texte sowie Zahlen mit print() und + * println() in verschiedenen Formaten ausgeben. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Serielle Kommunikation mit PC einrichten“ und + * „Ausgaben vom Arduino auf dem PC-Bildschirm“ (Formate BIN, HEX, Nachkommastellen). * * Beobachtung: - * Drei Ausgabebefehle erzeugen im Monitor zwei Textzeilen. + * „Hallo Arduino!“ steht in einer Zeile. Danach erscheint 42 dezimal, binär + * und hexadezimal sowie 3.14159 mit 2 und mit 4 Nachkommastellen. */ +int zahl = 42; +float wert = 3.14159; + void setup() { Serial.begin(115200); Serial.print("Hallo "); Serial.println("Arduino!"); - Serial.println("Zweite Zeile"); + + Serial.print("dezimal: "); + Serial.println(zahl); + Serial.print("binaer: "); + Serial.println(zahl, BIN); + Serial.print("hexadezimal: "); + Serial.println(zahl, HEX); + + Serial.print("2 Stellen: "); + Serial.println(wert, 2); + Serial.print("4 Stellen: "); + Serial.println(wert, 4); } void loop() { @@ -22,7 +42,8 @@ void loop() { /* * Experimente: * 1. Ersetze den ersten Aufruf von print() durch println(). - * 2. Aendere die Baudrate im Programm. + * 2. Gib zahl zusätzlich mit dem Format OCT aus. + * 3. Verschiebe die Ausgaben von setup() nach loop() und ergänze delay(1000);. * * Lernfragen: * 1. Was unterscheidet print() von println()? @@ -33,23 +54,25 @@ void loop() { /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen die serielle Verbindung starten und Text ausgeben können." + - "Die Studierenden sollen Texte und Zahlen seriell ausgeben und das Ausgabeformat wählen können." afterFocus: free focus: - id: print-und-println title: "print und println" - objective: "Den Unterschied zwischen `print` und `println` erklären und die Zeilenzahl der Ausgabe bestimmen." + objective: "Den Unterschied zwischen `print` und `println` erklären und die Zeilen der Ausgabe vorhersagen." questions: - kind: concept text: "Was unterscheidet `Serial.print` von `Serial.println`?" - - kind: application - text: "Wie viele Zeilen erscheinen im Monitor, und woran erkennst du das im Code?" - - id: initialisierung - title: "Serielle Verbindung starten" - objective: "Begründen, warum die Verbindung vor der ersten Ausgabe gestartet wird und warum der Text nur einmal erscheint." + - kind: prediction + text: "Wie sieht die erste Ausgabezeile aus, wenn der erste Aufruf von `Serial.print` durch `Serial.println` ersetzt wird?" + - id: formate + title: "Ausgabeformate" + objective: "Die Wirkung des zweiten Parameters bei der Ausgabe erklären." questions: - kind: concept - text: "Warum muss `Serial.begin` vor der ersten Ausgabe stehen?" - - kind: prediction - text: "Wie oft werden die Texte ausgegeben, und warum nicht fortlaufend?" + text: "Welche Aufgabe hat `BIN` in `Serial.println(zahl, BIN)`?" + - kind: application + text: "Wie viele Nachkommastellen erscheinen bei `Serial.println(wert, 4)`?" + - kind: transfer + text: "Wie würdest du eine Spannung mit genau einer Nachkommastelle ausgeben?" @end-unosim-tutor */ diff --git a/examples/IT03/it03-02-serielles-zeichen-einlesen.ino b/examples/IT03/it03-02-serielles-zeichen-einlesen.ino deleted file mode 100644 index b2547a8..0000000 --- a/examples/IT03/it03-02-serielles-zeichen-einlesen.ino +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IT03.2 - Ein Zeichen seriell einlesen - * - * Lernziel: - * Mit Serial.available() und Serial.read() ein einzelnes Zeichen empfangen. - * - * Beobachtung: - * Jedes gesendete Zeichen wird im seriellen Monitor wieder ausgegeben. - */ - -void setup() { - Serial.begin(115200); - Serial.println("Ein Zeichen eingeben:"); -} - -void loop() { - if (Serial.available() > 0) { - char zeichen = Serial.read(); - Serial.print("Empfangen: "); - Serial.println(zeichen); - } -} - -/* - * Experimente: - * 1. Sende nacheinander die Zeichen A, 7 und ?. - * 2. Aendere den Datentyp von zeichen zu int. - * - * Lernfragen: - * 1. Warum wird Serial.available() vor Serial.read() geprueft? - * 2. Was liefert Serial.read(), wenn kein Zeichen vorhanden ist? - * 3. Warum erscheinen bei einer int-Variablen Zahlen statt Zeichen? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen ein einzelnes Zeichen seriell einlesen und ausgeben können." -afterFocus: free -focus: - - id: zeichen-empfangen - title: "Zeichen empfangen" - objective: "Erklären, warum vor dem Lesen geprüft wird, ob ein Zeichen vorliegt, und was `loop` in der Zwischenzeit tut." - questions: - - kind: concept - text: "Warum wird `Serial.available` vor `Serial.read` geprüft?" - - kind: prediction - text: "Was geschieht in `loop`, solange kein Zeichen gesendet wurde?" - - id: datentyp-char - title: "Datentyp char" - objective: "Die Rolle von `char` erklären und die Folge eines anderen Datentyps vorhersagen." - questions: - - kind: concept - text: "Welche Rolle spielt der Datentyp `char` für die Variable `zeichen`?" - - kind: prediction - text: "Was würde sich in der Ausgabe ändern, wenn `zeichen` stattdessen eine Ganzzahl-Variable wäre?" -@end-unosim-tutor */ diff --git a/examples/IT03/it03-02-zeichen-einlesen.ino b/examples/IT03/it03-02-zeichen-einlesen.ino new file mode 100644 index 0000000..7034c30 --- /dev/null +++ b/examples/IT03/it03-02-zeichen-einlesen.ino @@ -0,0 +1,72 @@ +/* + * IT03.2 – Zeichen seriell einlesen + * + * Lernziel: + * Mit Serial.available() und Serial.read() einzelne Zeichen empfangen und + * Zeichen und ASCII-Code unterscheiden. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Serielle Eingabe“ und „Serielle Eingabe Beispiel int/char“. + * + * Beobachtung: + * Sende im seriellen Monitor den Text Hi. Es erscheinen H (Code 72) und + * i (Code 105). Danach kommt noch Code 10: das Zeilenende, das beim Senden + * angehängt wird – deshalb steht dort kein sichtbares Zeichen. + */ + +void setup() { + Serial.begin(115200); + Serial.println("Text eingeben und senden:"); +} + +void loop() { + if (Serial.available() > 0) { + char zeichen = Serial.read(); + int code = zeichen; + + Serial.print("Empfangen: "); + Serial.print(zeichen); + Serial.print(" Code: "); + Serial.println(code); + } +} + +/* + * Experimente: + * 1. Sende nacheinander A, 7 und ?. + * 2. Entferne die Abfrage if (Serial.available() > 0) samt Klammern und + * ergänze am Ende von loop() delay(500);. Was erscheint, solange nichts + * gesendet wird? + * 3. Sende ein Wort mit fünf Buchstaben. Wie oft wird loop() dafür mit einem + * empfangenen Zeichen durchlaufen? + * + * Lernfragen: + * 1. Warum wird Serial.available() vor Serial.read() geprüft? + * 2. Was liefert Serial.read(), wenn kein Zeichen vorhanden ist? + * 3. Warum erscheinen in der Variablen code Zahlen statt Zeichen? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einzelne Zeichen seriell einlesen und ausgeben können." + - "Sie sollen den Unterschied zwischen einem Zeichen und seinem Code erklären können." +afterFocus: free +focus: + - id: zeichen-empfangen + title: "Zeichen empfangen" + objective: "Erklären, warum vor dem Lesen geprüft wird, ob ein Zeichen vorliegt." + questions: + - kind: concept + text: "Warum wird `Serial.available` vor `Serial.read` geprüft?" + - kind: recall + text: "Welchen Wert liefert `Serial.read`, wenn kein Zeichen vorhanden ist?" + - id: zeichen-und-code + title: "Zeichen und Code" + objective: "Die Ausgabe von `zeichen` und `code` erklären." + questions: + - kind: concept + text: "Warum erscheint für `zeichen` ein Buchstabe, für `code` aber eine Zahl?" + - kind: prediction + text: "Welcher Code erscheint nach dem letzten gesendeten Buchstaben, und woher kommt er?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-03-digitaler-ausgang.ino b/examples/IT03/it03-03-digitaler-ausgang.ino deleted file mode 100644 index 3465aed..0000000 --- a/examples/IT03/it03-03-digitaler-ausgang.ino +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IT03.3 - Einen digitalen Ausgang einschalten - * - * Lernziel: - * Einen Pin als Ausgang konfigurieren und auf HIGH setzen. - * - * Beobachtung: - * Pin 13 wird im Simulator als eingeschalteter Ausgang angezeigt. - */ - -const byte PIN_LED = LED_BUILTIN; - -void setup() { - pinMode(PIN_LED, OUTPUT); - digitalWrite(PIN_LED, HIGH); -} - -void loop() { -} - -/* - * Experimente: - * 1. Ersetze HIGH durch LOW. - * 2. Verwende Pin 12 statt LED_BUILTIN. - * - * Lernfragen: - * 1. Welche Aufgabe hat pinMode()? - * 2. Welche elektrische Bedeutung haben HIGH und LOW? - * 3. Warum reicht fuer dieses Beispiel die Funktion setup() aus? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen erklären können, wie ein digitaler Ausgang im aktuellen Sketch gesetzt wird." - - "Sie sollen den Zusammenhang zwischen Ausgangszustand und beobachtbarer Wirkung beschreiben können." -afterFocus: free -focus: - - id: pin-konfigurieren - title: "Pin konfigurieren" - objective: "Erklären, wie ein Pin als Ausgang konfiguriert und auf einen Zustand gesetzt wird." - questions: - - kind: concept - text: "Welche Aufgabe hat `pinMode(PIN_LED, OUTPUT)`?" - - kind: concept - text: "Welche elektrische Bedeutung hat `HIGH` an diesem Pin?" - - kind: prediction - text: "Was würdest du im Simulator beobachten, wenn dort stattdessen der niedrige Pegel gesetzt würde?" - - id: programmstruktur - title: "Programmstruktur" - objective: "Begründen, warum `setup` genügt, und eine Erweiterung entwerfen." - questions: - - kind: concept - text: "Warum reicht für dieses Beispiel die Funktion `setup` aus?" - - kind: transfer - text: "Wie würdest du die LED wieder ausschalten?" -@end-unosim-tutor */ diff --git a/examples/IT03/it03-03-zahl-einlesen.ino b/examples/IT03/it03-03-zahl-einlesen.ino new file mode 100644 index 0000000..ca42eef --- /dev/null +++ b/examples/IT03/it03-03-zahl-einlesen.ino @@ -0,0 +1,68 @@ +/* + * IT03.3 – Eine Zahl am Stück einlesen + * + * Lernziel: + * Mit Serial.parseInt() eine mehrstellige Zahl einlesen und weiterverarbeiten. + * + * Bezug zu den Unterlagen: + * IT03, Folie „Zahlen am Stück einlesen mit Serial.parseInt()“ und + * Transferaufgabe „Pause Beginn / Pause Ende“. + * + * Beobachtung: + * Sende 21. Es erscheint „Zahl: 21, doppelt: 42“. + * Ohne Eingabe liefert parseInt() den Wert 0 – deshalb wird 0 ignoriert. + */ + +void setup() { + Serial.begin(115200); + Serial.println("Bitte eine Zahl eingeben:"); +} + +void loop() { + int zahl = Serial.parseInt(); + + if (zahl != 0) { + Serial.print("Zahl: "); + Serial.print(zahl); + Serial.print(", doppelt: "); + Serial.println(zahl * 2); + } +} + +/* + * Experimente: + * 1. Sende eine negative Zahl und den Text abc12. + * 2. Sende 0. Warum erscheint keine Ausgabe? + * 3. Erweitere das Programm wie in der Transferaufgabe: Die eingegebene Zahl + * ist eine Pausenzeit in ms. Gib „Pause Beginn“ aus, warte mit delay() + * und gib danach „Pause Ende“ aus. + * + * Lernfragen: + * 1. Warum ist parseInt() für mehrstellige Zahlen bequemer als read()? + * 2. Was macht parseInt() mit Zeichen, die keine Ziffern sind? + * 3. Welche Einschränkung entsteht durch die Bedingung zahl != 0? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Zahl mit parseInt() einlesen und weiterverarbeiten können." +afterFocus: free +focus: + - id: zahl-einlesen + title: "Zahl einlesen" + objective: "Die Arbeitsweise von `parseInt` beschreiben." + questions: + - kind: concept + text: "Warum ist `Serial.parseInt` für mehrstellige Zahlen bequemer als das Lesen einzelner Zeichen?" + - kind: prediction + text: "Welche Zahl erhält `zahl`, wenn der Text abc12 gesendet wird?" + - id: sonderfall-null + title: "Der Sonderfall 0" + objective: "Die Bedingung `zahl != 0` begründen und ihre Einschränkung erkennen." + questions: + - kind: concept + text: "Warum prüft das Programm `zahl != 0`, bevor es etwas ausgibt?" + - kind: transfer + text: "Wie würdest du das Programm verwenden, um eine eingegebene Pausenzeit abzuwarten?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-04-digitaler-eingang.ino b/examples/IT03/it03-04-digitaler-eingang.ino deleted file mode 100644 index e0d4efc..0000000 --- a/examples/IT03/it03-04-digitaler-eingang.ino +++ /dev/null @@ -1,59 +0,0 @@ -/* - * IT03.4 - Einen digitalen Eingang lesen - * - * Lernziel: - * Einen Eingang mit internem Pullup-Widerstand konfigurieren und lesen. - * - * Beobachtung: - * Der serielle Monitor zeigt den aktuellen Zustand von Pin 2. - */ - -const byte PIN_TASTER = 2; - -void setup() { - Serial.begin(115200); - pinMode(PIN_TASTER, INPUT_PULLUP); -} - -void loop() { - int zustand = digitalRead(PIN_TASTER); - Serial.println(zustand); - delay(500); -} - -/* - * Experimente: - * 1. Schalte den Eingang im Simulator zwischen HIGH und LOW um. - * 2. Ersetze INPUT_PULLUP durch INPUT. - * - * Lernfragen: - * 1. Welche Werte kann digitalRead() liefern? - * 2. Warum liefert ein gedrueckter Taster bei INPUT_PULLUP normalerweise LOW? - * 3. Welchen Zweck hat ein Pullup-Widerstand? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen einen Eingang mit internem Pullup-Widerstand lesen können." -afterFocus: free -focus: - - id: eingang-lesen - title: "Eingang lesen" - objective: "Die möglichen Werte von `digitalRead` nennen und den Zweck des Pullups erklären." - questions: - - kind: recall - text: "Welche Werte kann `digitalRead` liefern?" - - kind: concept - text: "Welchen Zweck hat `INPUT_PULLUP` für den Pin `PIN_TASTER`?" - - id: taster-logik - title: "Taster-Logik" - objective: "Den ausgegebenen Wert bei gedrücktem Taster vorhersagen und die Rolle der Wartezeit erklären." - questions: - - kind: prediction - text: "Welcher Wert wird ausgegeben, solange der Taster gedrückt ist, und warum?" - - kind: concept - text: "Warum steht in `loop` der Aufruf `delay(500)`?" - - kind: transfer - text: "Wie würdest du eine Meldung nur dann ausgeben, wenn der Taster gedrückt ist?" -@end-unosim-tutor */ diff --git a/examples/IT03/it03-04-led-blinken.ino b/examples/IT03/it03-04-led-blinken.ino new file mode 100644 index 0000000..03f0be7 --- /dev/null +++ b/examples/IT03/it03-04-led-blinken.ino @@ -0,0 +1,69 @@ +/* + * IT03.4 – Eine LED blinken lassen + * + * Lernziel: + * Einen Pin als Ausgang einrichten und in loop() abwechselnd ein- und + * ausschalten; Leucht- und Pausendauer als Konstanten festlegen. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Digitalen Arduino-Ausgang schalten mit digitalWrite“ und + * „Arduino-Beispielprogramm“; Übung Ü1.1. + * + * Beobachtung: + * Die LED an Pin 13 leuchtet 500 ms und ist dann 300 ms aus – endlos. + */ + +const byte PIN_LED = 13; +const int LEUCHTDAUER = 500; // ms +const int PAUSENDAUER = 300; // ms + +void setup() { + pinMode(PIN_LED, OUTPUT); +} + +void loop() { + digitalWrite(PIN_LED, HIGH); + delay(LEUCHTDAUER); + digitalWrite(PIN_LED, LOW); + delay(PAUSENDAUER); +} + +/* + * Experimente: + * 1. Stelle Leucht- und Pausendauer auf je 1000 ms ein (Ü1.1). + * 2. Lasse die LED dreimal kurz (200 ms) aufleuchten: das Morsezeichen S. + * 3. Entferne die letzte Zeile delay(PAUSENDAUER);. Warum scheint die LED + * jetzt dauerhaft zu leuchten? + * + * Lernfragen: + * 1. Welche Aufgabe hat pinMode()? + * 2. Warum steht das Blinken in loop() und nicht in setup()? + * 3. Was macht der Arduino während delay()? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen digitalen Ausgang zyklisch schalten können." + - "Sie sollen den Ablauf von setup() und loop() an einem Blinkprogramm erklären können." +afterFocus: free +focus: + - id: ausgang-schalten + title: "Ausgang schalten" + objective: "Die Aufgaben von `pinMode` und `digitalWrite` erklären." + questions: + - kind: concept + text: "Welche Aufgabe hat `pinMode(PIN_LED, OUTPUT)`?" + - kind: recall + text: "Welche Spannung liegt an `PIN_LED` an, solange `HIGH` geschrieben ist?" + - id: zeitlicher-ablauf + title: "Zeitlicher Ablauf" + objective: "Den Ablauf in `loop` beschreiben und eine Änderung vorhersagen." + questions: + - kind: concept + text: "Warum steht das Blinken in `loop` und nicht in `setup`?" + - kind: prediction + text: "Was beobachtest du, wenn die letzte Zeile `delay(PAUSENDAUER)` entfernt wird?" + - kind: transfer + text: "Wie würdest du das Programm ändern, um das Morsezeichen S auszugeben?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-05-millis.ino b/examples/IT03/it03-05-millis.ino deleted file mode 100644 index 688455b..0000000 --- a/examples/IT03/it03-05-millis.ino +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IT03.5 - Die vergangene Zeit abfragen - * - * Lernziel: - * Mit millis() die Zeit seit dem Programmstart bestimmen. - * - * Beobachtung: - * Der ausgegebene Zeitwert wird bei jeder Ausgabe groesser. - */ - -void setup() { - Serial.begin(115200); -} - -void loop() { - unsigned long zeitSeitStart = millis(); - Serial.println(zeitSeitStart); - delay(500); -} - -/* - * Experimente: - * 1. Aendere die Wartezeit auf 1000 ms. - * 2. Gib millis() vor und nach delay() aus. - * - * Lernfragen: - * 1. In welcher Einheit liefert millis() die Zeit? - * 2. Warum eignet sich unsigned long fuer den Zeitwert? - * 3. Wird der Zeitwert durch einen erneuten Schleifendurchlauf zurueckgesetzt? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen erklären können, warum millis() einen Zeitwert liefert." - - "Sie sollen den Zeitwert als Grundlage für einen wiederkehrenden Ablauf einordnen können." -afterFocus: free -focus: - - id: zeitwert - title: "Der Zeitwert" - objective: "Die Einheit von `millis()` nennen und das Wachstum des Werts über die Schleifendurchläufe erklären." - questions: - - kind: concept - text: "In welcher Einheit liefert `millis()` die Zeit?" - - kind: prediction - text: "Wie verändert sich der ausgegebene Wert von einem Schleifendurchlauf zum nächsten?" - - id: datentyp - title: "Der Datentyp" - objective: "Begründen, warum ein vorzeichenloser langer Ganzzahltyp für Zeitwerte passt, und das Verhalten über Durchläufe hinweg erklären." - questions: - - kind: concept - text: "Warum eignet sich `unsigned long` für `zeitSeitStart`?" - - kind: concept - text: "Wird der Zeitwert durch einen erneuten Durchlauf von `loop` zurückgesetzt?" - - kind: transfer - text: "Wie könntest du mit zwei `millis()`-Werten messen, wie lange etwas gedauert hat?" -@end-unosim-tutor */ diff --git a/examples/IT03/it03-05-pins-als-konstanten.ino b/examples/IT03/it03-05-pins-als-konstanten.ino new file mode 100644 index 0000000..ada59c5 --- /dev/null +++ b/examples/IT03/it03-05-pins-als-konstanten.ino @@ -0,0 +1,77 @@ +/* + * IT03.5 – Pin-Nummern als Konstanten + * + * Lernziel: + * Pin-Nummern einmal zentral als Konstanten festlegen – mit const oder mit + * der Präprozessoranweisung #define. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Konstanten“, „Konstanten für verwendete Pins“ und + * „Konstanten für verwendete Pins - Beispiel“ (3 LEDs). + * + * Beobachtung: + * Die LEDs an den Pins 5, 6 und 7 leuchten nacheinander auf (Lauflicht). + */ + +const byte PIN_LED1 = 5; +const byte PIN_LED2 = 6; +#define PIN_LED3 7 // ohne = und ohne ; – wird vor dem Compilieren ersetzt +const int WARTEZEIT = 300; // ms + +void setup() { + pinMode(PIN_LED1, OUTPUT); + pinMode(PIN_LED2, OUTPUT); + pinMode(PIN_LED3, OUTPUT); +} + +void loop() { + digitalWrite(PIN_LED1, HIGH); + delay(WARTEZEIT); + digitalWrite(PIN_LED1, LOW); + + digitalWrite(PIN_LED2, HIGH); + delay(WARTEZEIT); + digitalWrite(PIN_LED2, LOW); + + digitalWrite(PIN_LED3, HIGH); + delay(WARTEZEIT); + digitalWrite(PIN_LED3, LOW); +} + +/* + * Experimente: + * 1. Schließe die zweite LED an Pin 8 an. An wie vielen Stellen musst du + * das Programm ändern? + * 2. Schreibe #define PIN_LED3 = 7; und lies die Fehlermeldung. + * 3. Ergänze eine vierte LED und lasse das Licht auch zurücklaufen. + * + * Lernfragen: + * 1. Welchen Vorteil haben Konstanten für Pin-Nummern? + * 2. Was macht der Präprozessor mit PIN_LED3 vor dem Compilieren? + * 3. Warum steht bei #define kein = und kein ;? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen Pin-Nummern als Konstanten festlegen und den Nutzen begründen können." + - "Sie sollen `const` und `#define` unterscheiden können." +afterFocus: free +focus: + - id: nutzen + title: "Nutzen von Konstanten" + objective: "Begründen, warum Pin-Nummern als Konstanten festgelegt werden." + questions: + - kind: application + text: "An wie vielen Stellen musst du das Programm ändern, wenn `PIN_LED2` an einem anderen Pin angeschlossen wird?" + - kind: concept + text: "Warum ist `digitalWrite(PIN_LED1, HIGH)` besser lesbar als eine Zeile mit der Zahl 5?" + - id: const-und-define + title: "const und #define" + objective: "Die Arbeitsweise von `#define` beschreiben und von `const` abgrenzen." + questions: + - kind: concept + text: "Was macht der Präprozessor mit `PIN_LED3`, bevor compiliert wird?" + - kind: prediction + text: "Was geschieht, wenn in der Zeile mit `#define` ein Gleichheitszeichen und ein Semikolon ergänzt werden?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-06-digitaler-eingang.ino b/examples/IT03/it03-06-digitaler-eingang.ino new file mode 100644 index 0000000..cdec76d --- /dev/null +++ b/examples/IT03/it03-06-digitaler-eingang.ino @@ -0,0 +1,65 @@ +/* + * IT03.6 – Einen digitalen Eingang lesen + * + * Lernziel: + * Einen Eingang mit internem Pullup-Widerstand konfigurieren und seinen + * Zustand mit digitalRead() lesen. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Arduino-Pin einrichten mit pinMode“ und + * „Digitalen Arduino-Eingang lesen mit digitalRead“. + * + * Beobachtung: + * Der serielle Monitor zeigt alle 500 ms den Zustand von Pin 2: + * 1 bei offenem Eingang, 0 wenn der Eingang im Simulator auf LOW gesetzt wird. + */ + +const byte PIN_TASTER = 2; + +void setup() { + Serial.begin(115200); + pinMode(PIN_TASTER, INPUT_PULLUP); +} + +void loop() { + byte zustand = digitalRead(PIN_TASTER); + Serial.println(zustand); + delay(500); +} + +/* + * Experimente: + * 1. Schalte Pin 2 im Simulator zwischen HIGH und LOW um. + * 2. Ersetze INPUT_PULLUP durch INPUT. Welcher Zustand ist jetzt bei + * offenem Eingang zu erwarten? + * 3. Verkürze die Wartezeit auf 50 ms. + * + * Lernfragen: + * 1. Welche Werte kann digitalRead() liefern? + * 2. Warum liefert ein gedrückter Taster bei INPUT_PULLUP den Wert LOW? + * 3. Welchen Zweck hat ein Pullup-Widerstand? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen digitalen Eingang mit internem Pullup-Widerstand lesen können." +afterFocus: free +focus: + - id: eingang-lesen + title: "Eingang lesen" + objective: "Die möglichen Werte von `digitalRead` nennen und deuten." + questions: + - kind: recall + text: "Welche Werte kann `digitalRead` liefern?" + - kind: concept + text: "Warum genügt für `zustand` der Datentyp `byte`?" + - id: pullup + title: "Pullup-Widerstand" + objective: "Den Zweck von `INPUT_PULLUP` erklären und die Tasterlogik vorhersagen." + questions: + - kind: concept + text: "Welchen Zweck hat `INPUT_PULLUP` für den Pin `PIN_TASTER`?" + - kind: prediction + text: "Welcher Wert wird ausgegeben, solange ein Taster an `PIN_TASTER` gedrückt ist, und warum?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-07-zeit-messen.ino b/examples/IT03/it03-07-zeit-messen.ino new file mode 100644 index 0000000..8bfdd04 --- /dev/null +++ b/examples/IT03/it03-07-zeit-messen.ino @@ -0,0 +1,73 @@ +/* + * IT03.7 – Eine Zeitdifferenz messen + * + * Lernziel: + * Mit millis() zwei Zeitpunkte erfassen und daraus eine Dauer berechnen. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Zeit in Millisekunden abfragen mit millis“, + * „Zeit ausgeben“ und „Zeitdifferenz messen“. + * + * Beobachtung: + * Die Startzeit wächst von Ausgabe zu Ausgabe. Die gemessene Dauer beträgt + * ungefähr 587 ms – so lange dauert delay(587). + */ + +void setup() { + Serial.begin(115200); +} + +void loop() { + unsigned long zeit1 = millis(); + delay(587); + unsigned long zeit2 = millis(); + unsigned long dauer = zeit2 - zeit1; + + Serial.print("Startzeit: "); + Serial.print(zeit1); + Serial.print(" ms, Dauer: "); + Serial.print(dauer); + Serial.println(" ms"); + + delay(1000); +} + +/* + * Experimente: + * 1. Ersetze delay(587) durch drei Ausgaben mit einem langen Text. Wie lange + * dauert die Ausgabe? + * 2. Stelle die Baudrate auf 9600 und wiederhole das erste Experiment. + * 3. Gib zusätzlich die Startzeit in Sekunden aus. + * + * Lernfragen: + * 1. In welcher Einheit liefert millis() die Zeit? + * 2. Warum eignet sich unsigned long für Zeitwerte? + * 3. Warum steigt die Startzeit pro Durchlauf um etwas mehr als 1587 ms? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen mit millis() eine Zeitdifferenz messen können." + - "Sie sollen den Datentyp für Zeitwerte begründen können." +afterFocus: free +focus: + - id: zeitdifferenz + title: "Zeitdifferenz" + objective: "Erklären, wie aus zwei Zeitpunkten eine Dauer berechnet wird." + questions: + - kind: recall + text: "In welcher Einheit liefert `millis()` die Zeit?" + - kind: concept + text: "Warum ergibt `zeit2 - zeit1` die Dauer von `delay(587)`?" + - id: datentyp + title: "Datentyp für Zeiten" + objective: "Den Datentyp `unsigned long` für Zeitwerte begründen." + questions: + - kind: concept + text: "Warum eignet sich `unsigned long` für `zeit1` und `zeit2`?" + - kind: prediction + text: "Um wie viel ändert sich die Startzeit ungefähr von einer Ausgabe zur nächsten?" + - kind: transfer + text: "Wie würdest du messen, wie lange eine Ausgabe mit `Serial.print` dauert?" +@end-unosim-tutor */ diff --git a/examples/IT03/it03-08-map.ino b/examples/IT03/it03-08-map.ino new file mode 100644 index 0000000..5724f78 --- /dev/null +++ b/examples/IT03/it03-08-map.ino @@ -0,0 +1,75 @@ +/* + * IT03.8 – Werte mit map() umrechnen + * + * Lernziel: + * Mit map() einen Wert aus einem Wertebereich in einen anderen übertragen + * (Dreisatz) und die Ganzzahlmathematik von map() beachten. + * + * Bezug zu den Unterlagen: + * IT03, Folien „Dreisatz mit map()“ und „Zahl mappen“. + * + * Beobachtung: + * 23 im Bereich 0 bis 100 liegt dort, wo 230 im Bereich 0 bis 1000 liegt. + * Mit vertauschtem Zielbereich ergibt sich 770. Für 1 von 0 bis 3 auf + * 0 bis 10 erscheint 3 statt 3,33. + */ + +const int A_VON = 0; +const int A_BIS = 100; +const int B_VON = 0; +const int B_BIS = 1000; + +void setup() { + Serial.begin(115200); + + long gemappt = map(23, A_VON, A_BIS, B_VON, B_BIS); + Serial.println(gemappt); + + gemappt = map(23, A_VON, A_BIS, B_BIS, B_VON); + Serial.println(gemappt); + + long abgeschnitten = map(1, 0, 3, 0, 10); + Serial.println(abgeschnitten); +} + +void loop() { +} + +/* + * Experimente: + * 1. Bilde 50 und 100 ab. + * 2. Bilde den Wert 150 ab. Begrenzt map() das Ergebnis auf den Zielbereich? + * 3. Rechne einen Messwert von 0 bis 1023 in einen PWM-Wert von 0 bis 255 um. + * + * Lernfragen: + * 1. Welche Bedeutung haben die vier Bereichsgrenzen? + * 2. Was bewirkt das Vertauschen der Grenzen im Zielbereich? + * 3. Warum liefert map() keine Nachkommastellen? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen map() zur Umrechnung zwischen Wertebereichen anwenden können." + - "Sie sollen erklären können, dass map() mit ganzen Zahlen rechnet." +afterFocus: free +focus: + - id: bereichsgrenzen + title: "Bereichsgrenzen" + objective: "Die vier Bereichsgrenzen deuten und das Vertauschen erklären." + questions: + - kind: concept + text: "Welche Bedeutung haben `A_VON`, `A_BIS`, `B_VON` und `B_BIS` im Aufruf von `map`?" + - kind: concept + text: "Warum ergibt sich 770, wenn `B_BIS` und `B_VON` im Aufruf vertauscht werden?" + - id: ganzzahlen + title: "Ganzzahlmathematik" + objective: "Erklären, warum Nachkommastellen fehlen, und das Verhalten außerhalb des Bereichs vorhersagen." + questions: + - kind: concept + text: "Warum liefert `abgeschnitten` den Wert 3 und nicht 3,33?" + - kind: prediction + text: "Welches Ergebnis liefert `map` für den Wert 150, wenn der Ausgangsbereich von 0 bis 100 reicht?" + - kind: transfer + text: "Wie würdest du einen Messwert von 0 bis 1023 mit `map` in einen Wert von 0 bis 255 umrechnen?" +@end-unosim-tutor */ diff --git a/examples/IT04/it04-01-analoger-eingang.ino b/examples/IT04/it04-01-analoger-eingang.ino index 469904b..a8ef246 100644 --- a/examples/IT04/it04-01-analoger-eingang.ino +++ b/examples/IT04/it04-01-analoger-eingang.ino @@ -1,11 +1,16 @@ /* - * IT04.1 - Einen analogen Eingang lesen + * IT04.1 – Einen analogen Eingang lesen * * Lernziel: - * Einen Messwert mit analogRead() erfassen. + * Mit analogRead() eine Spannung am Pin A0 als 10-Bit-Wert erfassen. + * + * Bezug zu den Unterlagen: + * IT04, Folien „Was sind analoge Eingänge?“ und + * „Analogen Arduino-Eingang lesen mit analogRead“. * * Beobachtung: - * Der serielle Monitor zeigt fuer A0 einen Wert von 0 bis 1023. + * Der serielle Monitor zeigt alle 250 ms „A0=“ und einen Wert von 0 bis 1023. + * Stelle im Simulator verschiedene Werte für A0 ein. */ const byte PIN_SENSOR = A0; @@ -15,20 +20,22 @@ void setup() { } void loop() { - int messwert = analogRead(PIN_SENSOR); + unsigned int messwert = analogRead(PIN_SENSOR); + Serial.print("A0="); Serial.println(messwert); delay(250); } /* * Experimente: - * 1. Stelle A0 im Simulator auf 0, 512 und 1023. + * 1. Stelle A0 im Simulator auf 0, auf den Mittelwert und auf 1023. * 2. Lies statt A0 den Eingang A1. + * 3. Am realen Board: Verbinde A0 mit 5V und danach mit GND. * * Lernfragen: - * 1. Welchen Wertebereich besitzt analogRead() beim Arduino Uno? - * 2. Warum sind fuer diesen Bereich 10 Bit erforderlich? - * 3. Muss ein analoger Eingang mit pinMode() eingerichtet werden? + * 1. Welchen Wertebereich liefert analogRead() beim Arduino Uno? + * 2. Warum sind für diesen Bereich 10 Bit erforderlich? + * 3. Warum wurde für messwert unsigned int gewählt? */ /* @unosim-tutor @@ -44,13 +51,13 @@ focus: - kind: recall text: "Welchen Wertebereich liefert `analogRead` beim Arduino Uno?" - kind: concept - text: "Warum sind für diesen Bereich 10 Bit erforderlich?" - - id: pin-und-messwert - title: "Pin und Messwert" - objective: "Einordnen, wie ein Analogeingang eingerichtet wird, und einen Messwert abschätzen." + text: "Warum sind für diesen Wertebereich 10 Bit erforderlich?" + - id: messwert + title: "Messwert und Datentyp" + objective: "Den Datentyp begründen und einen Messwert abschätzen." questions: - kind: concept - text: "Muss ein analoger Eingang wie `PIN_SENSOR` mit pinMode() eingerichtet werden?" + text: "Warum ist `unsigned int` für `messwert` geeignet?" - kind: application - text: "Welchen Wert erwartest du für `messwert`, wenn A0 auf halber Referenzspannung liegt?" + text: "Welchen Wert erwartest du für `messwert`, wenn an `PIN_SENSOR` ungefähr 2,5 V anliegen?" @end-unosim-tutor */ diff --git a/examples/IT04/it04-02-messwert-in-volt.ino b/examples/IT04/it04-02-messwert-in-volt.ino deleted file mode 100644 index 9287b7a..0000000 --- a/examples/IT04/it04-02-messwert-in-volt.ino +++ /dev/null @@ -1,62 +0,0 @@ -/* - * IT04.2 - Einen Analogwert in Volt umrechnen - * - * Lernziel: - * Den digitalisierten Messwert eines Analogeingangs in eine Spannung umrechnen. - * - * Beobachtung: - * Zu jedem Rohwert von A0 wird die entsprechende Spannung ausgegeben. - */ - -const byte PIN_SENSOR = A0; -const float REFERENZSPANNUNG = 5.0; - -void setup() { - Serial.begin(115200); -} - -void loop() { - int messwert = analogRead(PIN_SENSOR); - float spannung = messwert * REFERENZSPANNUNG / 1024.0; - - Serial.print(messwert); - Serial.print(" entspricht "); - Serial.print(spannung, 2); - Serial.println(" V"); - delay(500); -} - -/* - * Experimente: - * 1. Stelle A0 auf den kleinsten und groessten Wert. - * 2. Entferne bei 1023.0 den Dezimalpunkt und vergleiche die Ausgabe. - * - * Lernfragen: - * 1. Welche Spannung gehoert ungefaehr zum Messwert 512? - * 2. Warum wird fuer spannung der Datentyp float verwendet? - * 3. Welche Bedeutung hat die Referenzspannung in der Rechnung? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen einen Rohwert in eine Spannung umrechnen können." -afterFocus: free -focus: - - id: umrechnung - title: "Umrechnung in Volt" - objective: "Die Spannung zu einem Rohwert abschätzen und die Rolle der Referenzspannung erklären." - questions: - - kind: prediction - text: "Welche Spannung gehört ungefähr zum Messwert 512?" - - kind: concept - text: "Welche Bedeutung hat `REFERENZSPANNUNG` in der Rechnung?" - - id: datentyp-float - title: "Gleitkommazahlen" - objective: "Begründen, warum `float` nötig ist, und die Folge einer Ganzzahlrechnung vorhersagen." - questions: - - kind: concept - text: "Warum wird für `spannung` der Datentyp `float` verwendet?" - - kind: prediction - text: "Was ändert sich im Ergebnis, wenn bei `1024.0` der Dezimalpunkt entfällt?" -@end-unosim-tutor */ diff --git a/examples/IT04/it04-02-spannung-berechnen.ino b/examples/IT04/it04-02-spannung-berechnen.ino new file mode 100644 index 0000000..a63a52b --- /dev/null +++ b/examples/IT04/it04-02-spannung-berechnen.ino @@ -0,0 +1,69 @@ +/* + * IT04.2 – Einen Messwert in Volt umrechnen + * + * Lernziel: + * Den digitalisierten Wert eines Analogeingangs in eine Spannung umrechnen + * und dabei Kommazahlen verwenden. + * + * Bezug zu den Unterlagen: + * IT04, Folien „Was sind analoge Eingänge?“ (567/1024*5V = 2,77 V) und + * „analogRead - Beispiel“ (Umrechnung in Volt). + * + * Beobachtung: + * Zu jedem Messwert erscheint die Spannung, z. B. „567 (2.77 V)“. + */ + +const byte PIN_SENSOR = A0; + +void setup() { + Serial.begin(115200); +} + +void loop() { + unsigned int messwert = analogRead(PIN_SENSOR); + float spannung = messwert / 1024.0 * 5.0; + + Serial.print(messwert); + Serial.print(" ("); + Serial.print(spannung, 2); + Serial.println(" V)"); + delay(500); +} + +/* + * Experimente: + * 1. Stelle A0 auf 567 und prüfe die Rechnung der Folie. + * 2. Ersetze 1024.0 durch 1024 und 5.0 durch 5. Was erscheint jetzt und warum? + * 3. Welche Spannung gehört zum Messwert 700? Prüfe deine Rechnung im Simulator. + * + * Lernfragen: + * 1. Warum wird durch 1024 geteilt und mit 5 multipliziert? + * 2. Warum wird für spannung der Datentyp float verwendet? + * 3. Warum hängt das Ergebnis im zweiten Experiment davon ab, ob 1024.0 oder + * 1024 geschrieben wird? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen Rohwert in eine Spannung umrechnen können." + - "Sie sollen erklären können, warum für die Rechnung Kommazahlen nötig sind." +afterFocus: free +focus: + - id: umrechnung + title: "Umrechnung in Volt" + objective: "Die Formel erklären und eine Spannung zu einem Messwert berechnen." + questions: + - kind: concept + text: "Warum wird `messwert` durch `1024.0` geteilt und mit `5.0` multipliziert?" + - kind: application + text: "Welche Spannung gehört zum Messwert 700?" + - id: kommazahlen + title: "Kommazahlen" + objective: "Begründen, warum `float` und Kommazahl-Konstanten nötig sind." + questions: + - kind: concept + text: "Warum wird für `spannung` der Datentyp `float` verwendet?" + - kind: prediction + text: "Welche Spannung wird angezeigt, wenn `1024.0` durch 1024 und `5.0` durch 5 ersetzt werden?" +@end-unosim-tutor */ diff --git a/examples/IT04/it04-03-pwm-ausgang.ino b/examples/IT04/it04-03-pwm-ausgang.ino index 326a99c..f412b92 100644 --- a/examples/IT04/it04-03-pwm-ausgang.ino +++ b/examples/IT04/it04-03-pwm-ausgang.ino @@ -1,14 +1,21 @@ /* - * IT04.3 - Einen Ausgang mit PWM ansteuern + * IT04.3 – Einen Ausgang mit PWM ansteuern * * Lernziel: - * Mit analogWrite() unterschiedliche PWM-Tastgrade einstellen. + * Mit analogWrite() an einem PWM-fähigen Pin eine veränderliche mittlere + * Spannung erzeugen. + * + * Bezug zu den Unterlagen: + * IT04, Folien „Analogen Arduino-Ausgang schreiben mit analogWrite“, + * „Info zur Puls-Weiten-Modulation“ und „analogWrite - Beispiel“ + * (aus, ca. 10 %, 100 %). * * Beobachtung: - * Pin 9 durchlaeuft nacheinander vier verschiedene PWM-Werte. + * Pin 11 ist nacheinander je 1 s aus (0), zu etwa 10 % (25) und + * voll (255) eingeschaltet. */ -const byte PIN_PWM = 9; +const byte PIN_PWM = 11; void setup() { pinMode(PIN_PWM, OUTPUT); @@ -17,9 +24,7 @@ void setup() { void loop() { analogWrite(PIN_PWM, 0); delay(1000); - analogWrite(PIN_PWM, 64); - delay(1000); - analogWrite(PIN_PWM, 128); + analogWrite(PIN_PWM, 25); delay(1000); analogWrite(PIN_PWM, 255); delay(1000); @@ -27,35 +32,38 @@ void loop() { /* * Experimente: - * 1. Teste zusaetzlich den PWM-Wert 192. - * 2. Verwende einen anderen PWM-faehigen Pin. + * 1. Ergänze den Wert 128. Welche mittlere Spannung entsteht? + * 2. Erzeuge an Pin 6 eine mittlere Spannung von 3,3 V. Welchen Wert brauchst du? + * 3. Lasse den Wert in kleinen Schritten von 0 bis 255 steigen. * * Lernfragen: * 1. Welchen Wertebereich erwartet analogWrite()? - * 2. Welchem Tastgrad entspricht ungefaehr der Wert 128? - * 3. Warum ist nicht jeder digitale Pin fuer PWM geeignet? + * 2. Welche Pins des Arduino Uno sind PWM-fähig? + * 3. Warum ändert PWM die mittlere Spannung, obwohl der Pin nur 0 V oder 5 V kennt? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen mit analogWrite() PWM-Tastgrade einstellen können." + - "Die Studierenden sollen mit analogWrite() eine mittlere Ausgangsspannung einstellen können." afterFocus: free focus: - - id: tastgrad + - id: wertebereich title: "Wertebereich und Tastgrad" objective: "Den Wertebereich von `analogWrite` nennen und Werte einem Tastgrad zuordnen." questions: - kind: recall text: "Welchen Wertebereich erwartet `analogWrite`?" - kind: concept - text: "Welchem Tastgrad entspricht ungefähr der Wert `128`?" - - id: pwm-pins - title: "PWM-Pins" - objective: "Erklären, warum nicht jeder Pin PWM kann, und die Wirkung unterschiedlicher Werte beschreiben." + text: "Warum entspricht der Wert 25 ungefähr einem Tastgrad von 10 %?" + - kind: application + text: "Welchen Wert brauchst du, um an einem PWM-Pin eine mittlere Spannung von 3,3 V zu erzeugen?" + - id: pwm-prinzip + title: "Prinzip der PWM" + objective: "Erklären, wie PWM eine mittlere Spannung erzeugt, und geeignete Pins nennen." questions: - kind: concept - text: "Warum ist nicht jeder digitale Pin für PWM geeignet?" - - kind: prediction - text: "Wie unterscheidet sich die Helligkeit einer LED bei `64` und bei `255`?" + text: "Warum ändert sich die mittlere Spannung an `PIN_PWM`, obwohl der Pin nur ein- oder ausgeschaltet ist?" + - kind: recall + text: "Welche Pins des Arduino Uno können mit `analogWrite` ein PWM-Signal ausgeben?" @end-unosim-tutor */ diff --git a/examples/IT04/it04-04-map.ino b/examples/IT04/it04-04-map.ino deleted file mode 100644 index 04ddd07..0000000 --- a/examples/IT04/it04-04-map.ino +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IT04.4 - Einen Wert auf einen anderen Bereich abbilden - * - * Lernziel: - * Mit map() die relative Lage eines Wertes auf einen neuen Wertebereich uebertragen. - * - * Beobachtung: - * Der Wert 512 aus dem Bereich 0 bis 1023 wird etwa auf 127 abgebildet. - */ - -int eingangswert = 512; - -void setup() { - Serial.begin(115200); - - long ausgangswert = map(eingangswert, 0, 1023, 0, 255); - Serial.println(ausgangswert); -} - -void loop() { -} - -/* - * Experimente: - * 1. Verwende die Eingangswerte 0 und 1023. - * 2. Vertausche im Zielbereich 0 und 255. - * 3. Probiere den Eingangswert 1200 aus. - * - * Lernfragen: - * 1. Welche Bedeutung haben die vier Bereichsgrenzen? - * 2. Warum ist das Ergebnis fuer 512 nicht exakt 127,5? - * 3. Begrenzt map() einen Wert automatisch auf den Zielbereich? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen map() zur Umrechnung zwischen Wertebereichen anwenden können." -afterFocus: free -focus: - - id: bereichsgrenzen - title: "Bereichsgrenzen" - objective: "Die vier Bereichsgrenzen deuten und das gerundete Ergebnis erklären." - questions: - - kind: concept - text: "Welche Bedeutung haben die vier Bereichsgrenzen im Aufruf von `map`?" - - kind: concept - text: "Warum ist das Ergebnis für 512 nicht exakt 127,5?" - - id: datentyp-und-grenzen - title: "Datentyp und Grenzen" - objective: "Die Rolle des Ergebnistyps erklären und das Verhalten außerhalb des Bereichs vorhersagen." - questions: - - kind: concept - text: "Welche Rolle spielt der Typ `long` für `ausgangswert`?" - - kind: prediction - text: "Begrenzt `map` einen Eingangswert wie 1200 automatisch auf den Zielbereich?" -@end-unosim-tutor */ diff --git a/examples/IT04/it04-04-taster-flanke.ino b/examples/IT04/it04-04-taster-flanke.ino new file mode 100644 index 0000000..a03c2e0 --- /dev/null +++ b/examples/IT04/it04-04-taster-flanke.ino @@ -0,0 +1,73 @@ +/* + * IT04.4 – Einen Tasterwechsel erkennen + * + * Lernziel: + * Einen Taster mit internem Pullup abfragen und nur bei einem Wechsel des + * Zustands (Flanke) reagieren. + * + * Bezug zu den Unterlagen: + * IT04, Folien „Bauteilwissen: Taster“, „Entprellen von Taster-Signalen“ + * und „Bauteilwissen: Taster abfragen“ (aktueller und alter Zustand, static). + * + * Beobachtung: + * Nur wenn Pin 3 im Simulator umgeschaltet wird, erscheint eine Ausgabe: + * 0 beim Drücken (Flanke 1 -> 0), 1 beim Loslassen. + */ + +const byte PIN_TASTER = 3; + +void setup() { + Serial.begin(115200); + pinMode(PIN_TASTER, INPUT_PULLUP); +} + +void loop() { + byte taster = digitalRead(PIN_TASTER); + static byte tasterAlt = HIGH; // behält seinen Wert zwischen den Durchläufen + + if (taster != tasterAlt) { + Serial.print("Neuer Zustand: "); + Serial.println(taster); + tasterAlt = taster; + } +} + +/* + * Experimente: + * 1. Entferne das Schlüsselwort static. Was erscheint jetzt, solange der + * Taster gedrückt ist? + * 2. Gib statt der Zahl „gedrueckt“ oder „losgelassen“ aus. + * 3. Ergänze nach tasterAlt = taster; ein delay(20); zum Entprellen. + * + * Lernfragen: + * 1. Warum werden zwei Variablen für den Tasterzustand benötigt? + * 2. Welche Aufgabe hat static bei tasterAlt? + * 3. Warum liefert der gedrückte Taster bei INPUT_PULLUP den Wert 0? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen Taster abfragen und einen Zustandswechsel erkennen können." + - "Sie sollen erklären können, warum der alte Zustand dauerhaft gespeichert werden muss." +afterFocus: free +focus: + - id: flanke + title: "Zustandswechsel erkennen" + objective: "Erklären, wie der Vergleich zweier Zustände einen Wechsel erkennt." + questions: + - kind: concept + text: "Warum werden `taster` und `tasterAlt` miteinander verglichen?" + - kind: prediction + text: "Wie oft erscheint eine Ausgabe, wenn der Taster gedrückt und zwei Sekunden lang gehalten wird?" + - id: static + title: "Den alten Zustand speichern" + objective: "Die Aufgabe von `static` erklären und die Folge ohne `static` vorhersagen." + questions: + - kind: concept + text: "Welche Aufgabe hat `static` bei `tasterAlt`?" + - kind: prediction + text: "Was erscheint, solange der Taster gedrückt ist, wenn `static` entfernt wird?" + - kind: transfer + text: "Wie würdest du zählen, wie oft der Taster gedrückt wurde?" +@end-unosim-tutor */ diff --git a/examples/IT04/it04-05-helligkeit-steuern.ino b/examples/IT04/it04-05-helligkeit-steuern.ino new file mode 100644 index 0000000..20ec277 --- /dev/null +++ b/examples/IT04/it04-05-helligkeit-steuern.ino @@ -0,0 +1,71 @@ +/* + * IT04.5 – Mit einem Sensorwert die Helligkeit steuern + * + * Lernziel: + * Einen analogen Messwert mit map() in einen PWM-Wert umrechnen und damit + * eine LED heller oder dunkler schalten. + * + * Bezug zu den Unterlagen: + * IT04, Folien „Bauteilewissen: Lichtsensor (LDR)“ und Transferaufgabe + * „LDR lesen und LED heller bzw. dunkler schalten“; IT03 „Dreisatz mit map()“. + * + * Beobachtung: + * Je höher der Wert an A0 im Simulator, desto größer der PWM-Wert an Pin 9: + * 0 ergibt 0, 1023 ergibt 255. + */ + +const byte PIN_LDR = A0; +const byte PIN_LED = 9; + +void setup() { + Serial.begin(115200); + pinMode(PIN_LED, OUTPUT); +} + +void loop() { + unsigned int licht = analogRead(PIN_LDR); + long helligkeit = map(licht, 0, 1023, 0, 255); + analogWrite(PIN_LED, helligkeit); + + Serial.print("Licht: "); + Serial.print(licht); + Serial.print(" -> PWM: "); + Serial.println(helligkeit); + delay(200); +} + +/* + * Experimente: + * 1. Vertausche im Zielbereich 0 und 255. Wie verhält sich die LED jetzt? + * 2. Ersetze map() durch die Rechnung licht / 4. Vergleiche die Ergebnisse. + * 3. Begrenze die Helligkeit auf höchstens 128. + * + * Lernfragen: + * 1. Warum ist eine Umrechnung zwischen analogRead() und analogWrite() nötig? + * 2. Welche Wirkkette verbindet Lichtänderung und LED-Helligkeit? + * 3. Warum ergibt licht / 4 fast dasselbe wie map()? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen einen Messwert in einen Stellwert umrechnen und ausgeben können." +afterFocus: free +focus: + - id: wertebereiche + title: "Zwei Wertebereiche" + objective: "Begründen, warum zwischen Eingangs- und Ausgangswert umgerechnet werden muss." + questions: + - kind: concept + text: "Warum kann `licht` nicht direkt an `analogWrite` übergeben werden?" + - kind: prediction + text: "Welchen Wert erhält `helligkeit`, wenn `licht` den Wert 512 hat?" + - id: wirkkette + title: "Wirkkette" + objective: "Die Wirkkette vom Sensor bis zur LED beschreiben und verändern." + questions: + - kind: application + text: "Welche Schritte liegen zwischen einer Lichtänderung am Sensor und einer Helligkeitsänderung an `PIN_LED`?" + - kind: transfer + text: "Wie würdest du das Programm ändern, damit die LED bei viel Licht dunkler wird?" +@end-unosim-tutor */ diff --git a/examples/IT05/it05-01-if.ino b/examples/IT05/it05-01-if.ino index 9dfdf28..41b8a88 100644 --- a/examples/IT05/it05-01-if.ino +++ b/examples/IT05/it05-01-if.ino @@ -1,42 +1,53 @@ /* - * IT05.1 - Eine Anweisung bedingt ausfuehren + * IT05.1 – Eine Anweisung bedingt ausführen * * Lernziel: - * Mit if einen Programmblock nur bei einer wahren Bedingung ausfuehren. + * Mit if einen Programmblock nur dann ausführen, wenn eine Bedingung wahr ist. + * + * Bezug zu den Unterlagen: + * IT05, Folien „Einfache Verzweigung - if“ und „Einfache Verzweigung - Bedingung“ + * (unwahr ist nur 0). * * Beobachtung: - * Die Meldung erscheint, weil der Messwert groesser als der Grenzwert ist. + * Der Messwert von A0 erscheint alle 500 ms. Liegt er über 600, steht + * dahinter zusätzlich „Grenzwert ueberschritten“. */ -int messwert = 75; -const int GRENZWERT = 60; +const byte PIN_SENSOR = A0; +const unsigned int GRENZWERT = 600; void setup() { Serial.begin(115200); +} + +void loop() { + unsigned int messwert = analogRead(PIN_SENSOR); + Serial.print(messwert); if (messwert > GRENZWERT) { - Serial.println("Grenzwert ueberschritten"); + Serial.print(" Grenzwert ueberschritten"); } -} -void loop() { + Serial.println(); + delay(500); } /* * Experimente: - * 1. Setze messwert auf 40. - * 2. Ersetze > durch >= und setze messwert auf 60. + * 1. Stelle A0 im Simulator auf 599, 600 und 601. + * 2. Ersetze > durch >= und wiederhole das erste Experiment. + * 3. Zeichne zu loop() ein UML-Aktivitätsdiagramm mit der Verzweigung. * * Lernfragen: - * 1. Welchen Datentyp hat die Bedingung messwert > GRENZWERT? - * 2. Was geschieht bei einer falschen Bedingung? - * 3. Wann unterscheiden sich > und >=? + * 1. Welchen Wahrheitswert hat die Bedingung bei messwert = 700? + * 2. Was geschieht, wenn die Bedingung falsch ist? + * 3. Warum steht Serial.println() außerhalb des if-Blocks? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen mit if einen Block bedingt ausführen können." + - "Die Studierenden sollen eine einfache Verzweigung mit if formulieren und ihren Ablauf beschreiben können." afterFocus: free focus: - id: bedingung @@ -44,15 +55,15 @@ focus: objective: "Den Wahrheitswert der Bedingung bestimmen und das Verhalten bei falscher Bedingung beschreiben." questions: - kind: prediction - text: "Welchen Wahrheitswert hat `messwert > GRENZWERT` mit den aktuellen Werten?" - - kind: prediction - text: "Was geschieht, wenn die Bedingung falsch ist?" - - id: grenzfall - title: "Der Grenzfall" - objective: "Den Unterschied zwischen „größer“ und „größer oder gleich“ am Grenzwert erklären." + text: "Welchen Wahrheitswert hat `messwert > GRENZWERT`, wenn A0 genau den Wert 600 liefert?" + - kind: concept + text: "Was geschieht mit dem Block nach `if`, wenn die Bedingung falsch ist?" + - id: ablauf + title: "Ablauf und Grenzfall" + objective: "Den Ablauf in `loop` beschreiben und die Bedingung anpassen." questions: - - kind: application - text: "Wann unterscheidet sich der Vergleich mit `>` von einem Vergleich mit „größer oder gleich“?" + - kind: concept + text: "Warum steht `Serial.println()` außerhalb des Blocks der Verzweigung?" - kind: transfer - text: "Wie würdest du eine zweite Meldung für Werte unterhalb des `GRENZWERT` ergänzen?" + text: "Wie müsste die Bedingung lauten, damit die Meldung schon beim Wert 600 erscheint?" @end-unosim-tutor */ diff --git a/examples/IT05/it05-02-if-else.ino b/examples/IT05/it05-02-if-else.ino index 464d824..c048ce9 100644 --- a/examples/IT05/it05-02-if-else.ino +++ b/examples/IT05/it05-02-if-else.ino @@ -1,58 +1,73 @@ /* - * IT05.2 - Zwischen zwei Alternativen entscheiden + * IT05.2 – Zwischen zwei Alternativen entscheiden * * Lernziel: - * Mit if/else genau einen von zwei Programmzweigen ausfuehren. + * Mit if/else genau einen von zwei Programmzweigen ausführen. + * + * Bezug zu den Unterlagen: + * IT05, Folien „Einfache Verzweigung – if/else“ (Syntax und Beispiel). * * Beobachtung: - * Abhaengig vom Messwert erscheint genau eine der beiden Meldungen. + * Liegt der Wert an A0 über 600, leuchtet die LED an Pin 13 und es erscheint + * „hoch“. Sonst ist die LED aus und es erscheint „nicht hoch“. */ -int messwert = 40; -const int GRENZWERT = 60; +const byte PIN_SENSOR = A0; +const byte PIN_LED = 13; +const unsigned int GRENZWERT = 600; void setup() { Serial.begin(115200); + pinMode(PIN_LED, OUTPUT); +} + +void loop() { + unsigned int messwert = analogRead(PIN_SENSOR); + Serial.print(messwert); if (messwert > GRENZWERT) { - Serial.println("Messwert ist hoch"); + digitalWrite(PIN_LED, HIGH); + Serial.println(" hoch"); } else { - Serial.println("Messwert ist nicht hoch"); + digitalWrite(PIN_LED, LOW); + Serial.println(" nicht hoch"); } -} -void loop() { + delay(500); } /* * Experimente: - * 1. Setze messwert auf 80. - * 2. Setze messwert genau auf 60. + * 1. Stelle A0 auf 800, danach genau auf 600. + * 2. Entferne den else-Zweig. Was passiert mit der LED, wenn der Wert wieder sinkt? + * 3. Zeichne zu loop() ein UML-Aktivitätsdiagramm mit beiden Zweigen. * * Lernfragen: - * 1. Koennen beide Zweige in einem Durchlauf ausgefuehrt werden? - * 2. Welcher Zweig wird bei messwert = 60 ausgefuehrt? - * 3. Wie muesste die Bedingung fuer "mindestens 60" lauten? + * 1. Können beide Zweige in einem Durchlauf ausgeführt werden? + * 2. Welcher Zweig wird bei messwert = 600 ausgeführt? + * 3. Warum wird der else-Zweig zum Ausschalten der LED gebraucht? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen mit if/else genau einen von zwei Zweigen ausführen können." + - "Die Studierenden sollen mit if/else zwischen zwei Alternativen entscheiden können." afterFocus: free focus: - - id: zwei-zweige - title: "Zwei Zweige" - objective: "Erklären, dass genau ein Zweig läuft, und den Grenzfall vorhersagen." + - id: genau-ein-zweig + title: "Genau ein Zweig" + objective: "Erklären, dass genau ein Zweig ausgeführt wird, und den Grenzfall vorhersagen." questions: - kind: concept - text: "Können der `if`-Zweig und der `else`-Zweig in einem Durchlauf beide ausgeführt werden?" + text: "Können der Zweig nach `if` und der Zweig nach `else` in einem Durchlauf beide ausgeführt werden?" - kind: prediction text: "Welcher Zweig wird ausgeführt, wenn `messwert` genau dem Wert von `GRENZWERT` entspricht?" - - id: bedingung-formulieren - title: "Bedingung formulieren" - objective: "Eine Bedingung für „mindestens“ formulieren." + - id: else-zweig + title: "Aufgabe des else-Zweigs" + objective: "Begründen, warum der `else`-Zweig für das Ausschalten nötig ist." questions: + - kind: prediction + text: "Was passiert mit der LED an `PIN_LED`, wenn der `else`-Zweig fehlt und der Messwert wieder sinkt?" - kind: transfer - text: "Wie müsste die Bedingung lauten, damit der Wert mindestens `GRENZWERT` entspricht?" + text: "Wie würdest du einen zweiten Grenzwert für eine Warnmeldung ergänzen?" @end-unosim-tutor */ diff --git a/examples/IT05/it05-03-else-if.ino b/examples/IT05/it05-03-else-if.ino new file mode 100644 index 0000000..ab07966 --- /dev/null +++ b/examples/IT05/it05-03-else-if.ino @@ -0,0 +1,74 @@ +/* + * IT05.3 – Mehrere Bedingungen nacheinander prüfen + * + * Lernziel: + * Mit if / else if / else eine Mehrfachverzweigung formulieren und die + * Reihenfolge der Bedingungen begründen. + * + * Bezug zu den Unterlagen: + * IT05, Folien „Mehrfach Verzweigung – if/else if/else“ (Syntax und Beispiel). + * + * Beobachtung: + * Je nach Wert an A0 erscheint „dunkel“ (unter 300), „mittel“ (unter 700) + * oder „hell“. + */ + +const byte PIN_SENSOR = A0; +const unsigned int GRENZE_DUNKEL = 300; +const unsigned int GRENZE_MITTEL = 700; + +void setup() { + Serial.begin(115200); +} + +void loop() { + unsigned int messwert = analogRead(PIN_SENSOR); + Serial.print(messwert); + + if (messwert < GRENZE_DUNKEL) { + Serial.println(" dunkel"); + } else if (messwert < GRENZE_MITTEL) { + Serial.println(" mittel"); + } else { + Serial.println(" hell"); + } + + delay(500); +} + +/* + * Experimente: + * 1. Stelle A0 auf 100, 500 und 900. + * 2. Vertausche die ersten beiden Bedingungen (zuerst GRENZE_MITTEL prüfen). + * Erscheint „dunkel“ noch? + * 3. Ergänze eine vierte Stufe „sehr hell“ ab 950. + * + * Lernfragen: + * 1. Warum wird die zweite Bedingung nur geprüft, wenn die erste falsch ist? + * 2. Warum braucht die zweite Bedingung keinen Vergleich mit GRENZE_DUNKEL? + * 3. Wann wird der letzte else-Zweig ausgeführt? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Mehrfachverzweigung mit else if formulieren und die Reihenfolge der Bedingungen begründen können." +afterFocus: free +focus: + - id: pruefreihenfolge + title: "Prüfreihenfolge" + objective: "Erklären, in welcher Reihenfolge die Bedingungen geprüft werden." + questions: + - kind: concept + text: "Warum wird `messwert < GRENZE_MITTEL` nur geprüft, wenn `messwert < GRENZE_DUNKEL` falsch ist?" + - kind: prediction + text: "Welche Ausgabe erscheint für den Messwert 300?" + - id: reihenfolge-aendern + title: "Reihenfolge ändern" + objective: "Die Folge einer vertauschten Reihenfolge vorhersagen und eine Stufe ergänzen." + questions: + - kind: prediction + text: "Erscheint „dunkel“ noch, wenn zuerst `GRENZE_MITTEL` und erst danach `GRENZE_DUNKEL` geprüft wird?" + - kind: transfer + text: "Wie würdest du eine vierte Stufe für sehr helles Licht ergänzen?" +@end-unosim-tutor */ diff --git a/examples/IT05/it05-03-switch.ino b/examples/IT05/it05-03-switch.ino deleted file mode 100644 index 5e7a88b..0000000 --- a/examples/IT05/it05-03-switch.ino +++ /dev/null @@ -1,67 +0,0 @@ -/* - * IT05.3 - Einen von mehreren Faellen auswaehlen - * - * Lernziel: - * Mit switch/case anhand eines Wertes einen Programmzweig auswaehlen. - * - * Beobachtung: - * Fuer modus = 2 wird die zweite Meldung ausgegeben. - */ - -byte modus = 2; - -void setup() { - Serial.begin(115200); - - switch (modus) { - case 1: - Serial.println("Modus 1: langsam"); - break; - case 2: - Serial.println("Modus 2: schnell"); - break; - default: - Serial.println("Unbekannter Modus"); - break; - } -} - -void loop() { -} - -/* - * Experimente: - * 1. Setze modus auf 1 und danach auf 7. - * 2. Entferne das erste break. - * - * Lernfragen: - * 1. Wann wird der default-Zweig ausgefuehrt? - * 2. Welche Aufgabe hat break? - * 3. Was geschieht ohne das erste break bei modus = 1? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen mit switch/case anhand eines Wertes einen Zweig auswählen können." -afterFocus: free -focus: - - id: default-zweig - title: "Der default-Zweig" - objective: "Erklären, wann `default` greift, und eine Ausgabe vorhersagen." - questions: - - kind: concept - text: "Wann wird der `default`-Zweig ausgeführt?" - - kind: prediction - text: "Welche Meldung erscheint, wenn `modus` den Wert 7 hat?" - - id: break - title: "Die Rolle von break" - objective: "Die Aufgabe von `break` erklären und das Durchfallen ohne `break` vorhersagen." - questions: - - kind: concept - text: "Welche Aufgabe hat `break` in einem `case`?" - - kind: prediction - text: "Was geschähe ohne das erste `break` bei `modus` gleich 1?" - - kind: transfer - text: "Wie würdest du einen dritten Modus ergänzen?" -@end-unosim-tutor */ diff --git a/examples/IT05/it05-04-switch-menue.ino b/examples/IT05/it05-04-switch-menue.ino new file mode 100644 index 0000000..34506ac --- /dev/null +++ b/examples/IT05/it05-04-switch-menue.ino @@ -0,0 +1,86 @@ +/* + * IT05.4 – Ein Menü mit switch + * + * Lernziel: + * Mit switch/case anhand einer eingegebenen Zahl einen von mehreren Fällen + * auswählen und die Aufgabe von break und default erklären. + * + * Bezug zu den Unterlagen: + * IT05, Folien „Mehrfachverzweigung - switch“ und „Mehrfachverzweigung - + * Beispiel“ (Menübedienfunktion); IT03 „Serial.parseInt()“. + * + * Beobachtung: + * Sende 1, 2 oder 3: LED an, LED aus oder Zustand anzeigen. + * Jede andere Zahl ergibt „Unbekannte Auswahl“. + */ + +const byte PIN_LED = 13; + +void setup() { + Serial.begin(115200); + pinMode(PIN_LED, OUTPUT); + Serial.println("1 = LED an, 2 = LED aus, 3 = Zustand"); +} + +void loop() { + int auswahl = Serial.parseInt(); // liefert 0, wenn nichts eingegeben wurde + + switch (auswahl) { + case 0: + break; // keine Eingabe: nichts tun + case 1: + digitalWrite(PIN_LED, HIGH); + Serial.println("LED an"); + break; + case 2: + digitalWrite(PIN_LED, LOW); + Serial.println("LED aus"); + break; + case 3: + Serial.print("LED-Zustand: "); + Serial.println(digitalRead(PIN_LED)); + break; + default: + Serial.println("Unbekannte Auswahl"); + break; + } +} + +/* + * Experimente: + * 1. Sende 1, 3, 2, 3 und 7. + * 2. Entferne das break nach Serial.println("LED an"); und sende 1. + * 3. Ergänze einen Fall 4, der die LED umschaltet. + * + * Lernfragen: + * 1. Wann wird der default-Zweig ausgeführt? + * 2. Welche Aufgabe hat break? + * 3. Warum gibt es einen eigenen Fall für 0? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Menüauswahl mit switch/case umsetzen können." + - "Sie sollen die Aufgaben von break und default erklären können." +afterFocus: free +focus: + - id: faelle + title: "Fälle und default" + objective: "Erklären, welcher Fall bei einer Eingabe ausgeführt wird." + questions: + - kind: concept + text: "Wann wird der `default`-Zweig ausgeführt?" + - kind: concept + text: "Warum gibt es einen eigenen Fall für den Wert 0 von `auswahl`?" + - id: break + title: "Die Aufgabe von break" + objective: "Die Aufgabe von `break` erklären und das Durchfallen vorhersagen." + questions: + - kind: concept + text: "Welche Aufgabe hat `break` am Ende eines Falls?" + - kind: prediction + text: "Was geschieht, wenn nach dem Fall 1 das `break` fehlt und 1 gesendet wird?" + - kind: transfer + text: "Wie würdest du einen Fall ergänzen, der die LED an `PIN_LED` umschaltet?" +@end-unosim-tutor */ diff --git a/examples/IT05/it05-05-intervall-ohne-delay.ino b/examples/IT05/it05-05-intervall-ohne-delay.ino new file mode 100644 index 0000000..59fd3ac --- /dev/null +++ b/examples/IT05/it05-05-intervall-ohne-delay.ino @@ -0,0 +1,75 @@ +/* + * IT05.5 – Alle 500 ms etwas tun, ohne delay() + * + * Lernziel: + * Mit millis() und if eine Aktion in festen Zeitabständen ausführen, ohne + * das Programm mit delay() anzuhalten. + * + * Bezug zu den Unterlagen: + * IT05, Folie „Einfache Verzweigung - Beispiel“ (alle 500 ms eine Ausgabe + * ohne delay); IT03 „Zeit in Millisekunden abfragen mit millis“. + * + * Beobachtung: + * Alle 500 ms erscheint eine Zeile. Zwischen zwei Ausgaben wird loop() sehr + * oft durchlaufen – das Programm wartet nicht. Wie oft, hängt von der + * Geschwindigkeit des Systems ab. + */ + +const unsigned long INTERVALL = 500; // ms +unsigned long letzteAusgabe = 0; +unsigned long durchlaeufe = 0; + +void setup() { + Serial.begin(115200); +} + +void loop() { + durchlaeufe = durchlaeufe + 1; + unsigned long jetzt = millis(); + + if (jetzt - letzteAusgabe >= INTERVALL) { + letzteAusgabe = jetzt; + Serial.print("Zeit: "); + Serial.print(jetzt); + Serial.print(" ms, Durchlaeufe: "); + Serial.println(durchlaeufe); + } +} + +/* + * Experimente: + * 1. Ändere INTERVALL auf 2000. + * 2. Ersetze die Verzweigung durch die Ausgabe und delay(500);. Vergleiche + * die Zahl der Durchläufe. + * 3. Lasse zusätzlich die LED an Pin 13 im selben Takt umschalten. + * + * Lernfragen: + * 1. Warum wird jetzt - letzteAusgabe berechnet und nicht nur jetzt geprüft? + * 2. Warum muss letzteAusgabe nach jeder Ausgabe neu gesetzt werden? + * 3. Welchen Vorteil hat diese Lösung gegenüber delay()? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine zeitgesteuerte Aktion ohne delay() umsetzen können." + - "Sie sollen erklären können, warum das Programm dabei weiterläuft." +afterFocus: free +focus: + - id: zeitvergleich + title: "Der Zeitvergleich" + objective: "Die Bedingung mit `millis` erklären." + questions: + - kind: concept + text: "Warum prüft die Bedingung `jetzt - letzteAusgabe >= INTERVALL`?" + - kind: concept + text: "Warum muss `letzteAusgabe` nach jeder Ausgabe auf `jetzt` gesetzt werden?" + - id: vorteil + title: "Vorteil gegenüber delay" + objective: "Den Unterschied zu einer festen Pause beschreiben und die Lösung erweitern." + questions: + - kind: prediction + text: "Wie verändert sich die Zahl in `durchlaeufe` zwischen zwei Ausgaben, wenn das Programm stattdessen nach jeder Ausgabe 500 ms anhält?" + - kind: transfer + text: "Wie würdest du zusätzlich eine LED im selben Takt umschalten, ohne das Programm anzuhalten?" +@end-unosim-tutor */ diff --git a/examples/IT06/it06-01-while.ino b/examples/IT06/it06-01-while.ino index d15932e..9022667 100644 --- a/examples/IT06/it06-01-while.ino +++ b/examples/IT06/it06-01-while.ino @@ -1,21 +1,36 @@ /* - * IT06.1 - Eine kopfgesteuerte Schleife verwenden + * IT06.1 – Eine kopfgesteuerte Schleife * * Lernziel: - * Einen Anweisungsblock mit while wiederholen, solange eine Bedingung wahr ist. + * Mit while einen Block wiederholen, solange eine Bedingung wahr ist, auch + * wenn die Anzahl der Durchläufe vorher nicht bekannt ist. + * + * Bezug zu den Unterlagen: + * IT06, Folien „Kopfgesteuerte Schleife“ und „Kopfgesteuerte Schleife - while“. * * Beobachtung: - * Der serielle Monitor zeigt die Zahlen 0 bis 4. + * Der Wert wird verdoppelt, solange er kleiner als 1000 ist: + * 1, 2, 4, ..., 512. Danach erscheinen „Schritte: 10“ und „Endwert: 1024“. */ +const unsigned int GRENZE = 1000; + void setup() { Serial.begin(115200); - byte zaehler = 0; - while (zaehler < 5) { - Serial.println(zaehler); - zaehler++; + unsigned int wert = 1; + byte schritte = 0; + + while (wert < GRENZE) { + Serial.println(wert); + wert = wert * 2; + schritte++; } + + Serial.print("Schritte: "); + Serial.println(schritte); + Serial.print("Endwert: "); + Serial.println(wert); } void loop() { @@ -23,35 +38,38 @@ void loop() { /* * Experimente: - * 1. Aendere die Schleifenbedingung zu zaehler < 10. - * 2. Setze den Startwert des Zaehlers auf 5. + * 1. Setze den Startwert von wert auf 3. + * 2. Setze den Startwert von wert auf 2000. Wie oft läuft die Schleife? + * 3. Entferne die Zeile wert = wert * 2;. Was geschieht? (Simulation danach stoppen.) * * Lernfragen: - * 1. Wann wird die Schleifenbedingung geprueft? - * 2. Warum muss zaehler im Schleifenblock veraendert werden? - * 3. Wie oft laeuft die Schleife beim Startwert 5? + * 1. Wann wird die Bedingung einer while-Schleife geprüft? + * 2. Warum muss wert im Schleifenblock verändert werden? + * 3. Warum ist der Endwert größer als GRENZE? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen eine kopfgesteuerte Schleife mit while schreiben und deuten können." + - "Die Studierenden sollen eine kopfgesteuerte Schleife mit while formulieren und ihren Ablauf beschreiben können." afterFocus: free focus: - - id: bedingungspruefung - title: "Bedingungsprüfung" + - id: pruefung + title: "Prüfung am Kopf" objective: "Erklären, wann die Bedingung geprüft wird, und die Zahl der Durchläufe vorhersagen." questions: - kind: concept - text: "Wann wird die Bedingung `zaehler < 5` geprüft?" + text: "Wann wird die Bedingung `wert < GRENZE` geprüft?" - kind: prediction - text: "Wie oft läuft die Schleife, wenn `zaehler` mit dem Wert 5 startet?" - - id: zaehler - title: "Der Zähler" - objective: "Begründen, warum der Zähler im Block verändert werden muss." + text: "Wie oft läuft die Schleife, wenn `wert` mit 2000 startet?" + - id: abbruch + title: "Abbruch der Schleife" + objective: "Begründen, warum sich die Bedingung im Block ändern muss." questions: - kind: concept - text: "Warum muss `zaehler` im Schleifenblock verändert werden?" - - kind: prediction - text: "Welche Zahlen erscheinen im Monitor?" + text: "Warum muss `wert` im Schleifenblock verändert werden?" + - kind: concept + text: "Warum ist der ausgegebene Endwert größer als `GRENZE`?" + - kind: transfer + text: "Wie würdest du mit einer `while`-Schleife zählen, wie viele Halbierungen von 1000 bis unter 1 nötig sind?" @end-unosim-tutor */ diff --git a/examples/IT06/it06-02-do-while.ino b/examples/IT06/it06-02-do-while.ino index ac98046..2819368 100644 --- a/examples/IT06/it06-02-do-while.ino +++ b/examples/IT06/it06-02-do-while.ino @@ -1,21 +1,35 @@ /* - * IT06.2 - Eine fussgesteuerte Schleife verwenden + * IT06.2 – Eine fußgesteuerte Schleife * * Lernziel: - * Einen Anweisungsblock mit do/while mindestens einmal ausfuehren. + * Mit do/while einen Block mindestens einmal ausführen und erst danach die + * Bedingung prüfen. + * + * Bezug zu den Unterlagen: + * IT06, Folien „Fußgesteuerte Schleife“ und „Fußgesteuerte Schleife – + * do {} while“; Übung LED-Würfel. * * Beobachtung: - * Trotz einer anfangs falschen Bedingung erscheint eine Ausgabe. + * Es wird so lange gewürfelt, bis eine 6 fällt. Mindestens ein Wurf findet + * immer statt. Danach steht die Zahl der Versuche im Monitor. + * random(1, 7) liefert eine Zufallszahl von 1 bis 6. */ void setup() { Serial.begin(115200); - byte zaehler = 5; + byte wurf; + byte versuche = 0; + do { - Serial.println(zaehler); - zaehler++; - } while (zaehler < 5); + wurf = random(1, 7); + versuche++; + Serial.print("Wurf: "); + Serial.println(wurf); + } while (wurf != 6); + + Serial.print("Versuche bis zur 6: "); + Serial.println(versuche); } void loop() { @@ -23,19 +37,21 @@ void loop() { /* * Experimente: - * 1. Setze den Startwert auf 0. - * 2. Ersetze do/while durch eine while-Schleife mit gleicher Bedingung. + * 1. Würfle so lange, bis eine 1 fällt. + * 2. Schreibe die Schleife als while-Schleife mit derselben Bedingung und + * setze wurf vorher auf 6. Wie oft wird jetzt gewürfelt? + * 3. Verschiebe den Würfelvorgang nach loop() und ergänze delay(2000);. * * Lernfragen: - * 1. Wann wird die Bedingung einer do/while-Schleife geprueft? - * 2. Wie oft wird ihr Block mindestens ausgefuehrt? - * 3. Warum entsteht beim zweiten Experiment mit Startwert 5 keine Ausgabe? + * 1. Wann wird die Bedingung einer do/while-Schleife geprüft? + * 2. Wie oft wird ihr Block mindestens ausgeführt? + * 3. Warum passt eine fußgesteuerte Schleife gut zum Würfeln? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen eine fußgesteuerte Schleife mit do/while deuten können." + - "Die Studierenden sollen eine fußgesteuerte Schleife mit do/while einsetzen und von while unterscheiden können." afterFocus: free focus: - id: pruefung-am-ende @@ -43,13 +59,15 @@ focus: objective: "Erklären, wann die Bedingung geprüft wird und wie oft der Block mindestens läuft." questions: - kind: concept - text: "Wann wird die Bedingung der `do`/`while`-Schleife geprüft?" - - kind: prediction - text: "Wie oft wird der Block mindestens ausgeführt?" + text: "Wann wird die Bedingung `wurf != 6` geprüft?" + - kind: recall + text: "Wie oft wird der Block der `do`-Schleife mindestens ausgeführt?" - id: vergleich-while title: "Vergleich mit while" - objective: "Das Verhalten von do/while mit while bei gleichem Startwert vergleichen." + objective: "Das Verhalten von do/while und while bei gleicher Bedingung vergleichen." questions: + - kind: prediction + text: "Wie oft wird gewürfelt, wenn eine `while`-Schleife mit derselben Bedingung verwendet wird und `wurf` vorher den Wert 6 hat?" - kind: concept - text: "Warum entstünde mit einer reinen `while`-Schleife und demselben Startwert keine Ausgabe?" + text: "Warum passt eine fußgesteuerte Schleife gut zum Würfeln bis zur 6?" @end-unosim-tutor */ diff --git a/examples/IT06/it06-03-for.ino b/examples/IT06/it06-03-for.ino index 4b7fe66..4b7f509 100644 --- a/examples/IT06/it06-03-for.ino +++ b/examples/IT06/it06-03-for.ino @@ -1,19 +1,38 @@ /* - * IT06.3 - Eine Zaehlerschleife verwenden + * IT06.3 – Zählschleifen mit for * * Lernziel: - * Eine feste Anzahl von Wiederholungen mit for formulieren. + * Startwert, Abbruchbedingung und Schrittweite einer for-Schleife festlegen, + * auch für Schrittweite 2 und für das Rückwärtszählen. + * + * Bezug zu den Unterlagen: + * IT06, Folien „Zählschleife - for“ (Syntax, Erläuterungen, geänderte + * Laufgrenzen, Schrittweite 2, Schleife zählt runter). * * Beobachtung: - * Der serielle Monitor zeigt die Zahlen 1 bis 5. + * Drei Zeilen: 0 bis 9, die geraden Zahlen von 0 bis 18 und 7 bis 0. */ void setup() { Serial.begin(115200); - for (byte i = 1; i <= 5; i++) { - Serial.println(i); + for (byte i = 0; i < 10; i++) { + Serial.print(i); + Serial.print(" "); + } + Serial.println(); + + for (byte i = 0; i < 20; i = i + 2) { + Serial.print(i); + Serial.print(" "); } + Serial.println(); + + for (int i = 7; i >= 0; i--) { + Serial.print(i); + Serial.print(" "); + } + Serial.println(); } void loop() { @@ -21,35 +40,40 @@ void loop() { /* * Experimente: - * 1. Lasse die Schleife von 0 bis 9 laufen. - * 2. Zaehle mit i-- von 5 bis 1 herunter. + * 1. Lasse die erste Schleife von 1 bis 10 laufen. + * 2. Zähle in der zweiten Schleife in Dreierschritten. + * 3. Ersetze in der dritten Schleife int durch byte. Was geschieht? + * (Simulation danach stoppen.) * * Lernfragen: - * 1. Welche drei Angaben stehen im Kopf der for-Schleife? - * 2. Wie oft wird der Schleifenblock ausgefuehrt? - * 3. Welchen Gueltigkeitsbereich hat die Variable i? + * 1. Welche drei Angaben stehen im Kopf einer for-Schleife? + * 2. Wie oft wird der Block der ersten Schleife ausgeführt? + * 3. Warum endet die dritte Schleife mit byte nie? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen eine Zählschleife mit for formulieren können." + - "Die Studierenden sollen for-Schleifen mit verschiedenen Laufgrenzen und Schrittweiten formulieren können." + - "Sie sollen erklären können, warum der Datentyp der Laufvariablen wichtig ist." afterFocus: free focus: - id: schleifenkopf title: "Der Schleifenkopf" objective: "Die drei Angaben im Schleifenkopf benennen und die Zahl der Durchläufe bestimmen." questions: - - kind: concept - text: "Welche drei Angaben stehen im Kopf von `for (byte i = 1; i <= 5; i++)`?" + - kind: recall + text: "Welche drei Angaben stehen im Kopf von `for (byte i = 0; i < 10; i++)`?" - kind: prediction - text: "Wie oft wird der Schleifenblock ausgeführt?" - - id: gueltigkeit - title: "Gültigkeit und Richtung" - objective: "Den Gültigkeitsbereich der Zählvariablen erklären und die Zählrichtung ändern." + text: "Welche Zahlen gibt die Schleife mit `i = i + 2` aus?" + - id: rueckwaerts + title: "Rückwärts zählen" + objective: "Die Bedingung beim Rückwärtszählen und den Datentyp der Laufvariablen begründen." questions: - kind: concept - text: "Welchen Gültigkeitsbereich hat die Variable `i`?" + text: "Warum endet die Schleife mit `i--` nach der Ausgabe von 0?" + - kind: prediction + text: "Was geschieht, wenn `i` in der Schleife mit `i >= 0` vom Typ `byte` ist?" - kind: transfer - text: "Wie würdest du die Schleife von 5 bis 1 herunterzählen lassen?" + text: "Wie würdest du eine Schleife schreiben, die von 20 in Zweierschritten bis 2 herunterzählt?" @end-unosim-tutor */ diff --git a/examples/IT06/it06-04-binaerzahl.ino b/examples/IT06/it06-04-binaerzahl.ino new file mode 100644 index 0000000..83261e8 --- /dev/null +++ b/examples/IT06/it06-04-binaerzahl.ino @@ -0,0 +1,70 @@ +/* + * IT06.4 – Eine Zahl binär ausgeben + * + * Lernziel: + * Mit einer Zählschleife und den bitweisen Operatoren alle 8 Bits einer + * Zahl ausgeben – auch die führenden Nullen. + * + * Bezug zu den Unterlagen: + * IT06, Abschnitt „Beispiel 1: Anwendung von Schleifen bei dem Programm zur + * Darstellung von Binärzahlen“; IT02 „Beispiel Dezimalzahl 37 als Binärzahl“. + * + * Beobachtung: + * „37 = 00100101“. Zum Vergleich gibt BIN die Zahl ohne führende Nullen aus. + */ + +byte a = 37; + +void setup() { + Serial.begin(115200); + + Serial.print(a); + Serial.print(" = "); + for (int i = 7; i >= 0; i--) { + Serial.print(a & (1 << i) && 1); // ist Bit i gesetzt? + } + Serial.println(); + + Serial.print("Mit BIN: "); + Serial.println(a, BIN); +} + +void loop() { +} + +/* + * Experimente: + * 1. Gib die Zahlen 200 und 255 aus. + * 2. Ergänze nach dem vierten Bit ein Leerzeichen (wie auf der Folie). + * 3. Erweitere das Programm auf 16 Bit: unsigned int a = 32109; und i ab 15. + * + * Lernfragen: + * 1. Welche Zahl ergibt 1 << i für i = 7? + * 2. Warum zählt die Schleife von 7 herunter und nicht von 0 hoch? + * 3. Welchen Vorteil hat die Schleife gegenüber acht einzelnen Ausgaben? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Zählschleife mit bitweisen Operatoren kombinieren können." + - "Sie sollen den Vorteil einer Schleife für die Skalierbarkeit eines Programms erklären können." +afterFocus: free +focus: + - id: bit-pruefen + title: "Ein Bit prüfen" + objective: "Erklären, wie in jedem Durchlauf ein Bit geprüft wird." + questions: + - kind: concept + text: "Welche Aufgabe hat `1 << i` in der Schleife?" + - kind: prediction + text: "Was wird im ersten Durchlauf ausgegeben, wenn `a` den Wert 200 hat?" + - id: schleife-und-skalierung + title: "Schleife und Skalierbarkeit" + objective: "Die Zählrichtung begründen und das Programm auf 16 Bit übertragen." + questions: + - kind: concept + text: "Warum zählt die Schleife mit `i--` von 7 herunter?" + - kind: transfer + text: "Was musst du ändern, damit alle 16 Bits einer größeren Zahl ausgegeben werden?" +@end-unosim-tutor */ diff --git a/examples/IT06/it06-04-verschachtelte-schleifen.ino b/examples/IT06/it06-04-verschachtelte-schleifen.ino deleted file mode 100644 index b60ed8c..0000000 --- a/examples/IT06/it06-04-verschachtelte-schleifen.ino +++ /dev/null @@ -1,58 +0,0 @@ -/* - * IT06.4 - Zwei Schleifen verschachteln - * - * Lernziel: - * Eine innere Schleife fuer jeden Durchlauf einer aeusseren Schleife ausfuehren. - * - * Beobachtung: - * Der serielle Monitor zeigt ein Rechteck aus drei Zeilen und vier Zeichen. - */ - -void setup() { - Serial.begin(115200); - - for (byte zeile = 0; zeile < 3; zeile++) { - for (byte spalte = 0; spalte < 4; spalte++) { - Serial.print("X"); - } - Serial.println(); - } -} - -void loop() { -} - -/* - * Experimente: - * 1. Erzeuge ein Rechteck aus zwei Zeilen und sechs Spalten. - * 2. Gib statt X den aktuellen Spaltenindex aus. - * - * Lernfragen: - * 1. Wie oft wird die innere Schleife insgesamt durchlaufen? - * 2. Welche Schleife bestimmt die Anzahl der Zeilen? - * 3. Warum steht println() ausserhalb der inneren Schleife? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen zwei Schleifen verschachteln können." -afterFocus: free -focus: - - id: verschachtelung - title: "Verschachtelung" - objective: "Die Gesamtzahl der inneren Durchläufe bestimmen und die Rolle beider Schleifen erklären." - questions: - - kind: prediction - text: "Wie oft wird die innere Schleife insgesamt durchlaufen?" - - kind: concept - text: "Welche Schleife bestimmt die Anzahl der Zeilen?" - - id: zeilenumbruch - title: "Zeilenumbruch" - objective: "Die Stellung des Zeilenumbruchs begründen und das Muster verändern." - questions: - - kind: concept - text: "Warum steht `Serial.println()` außerhalb der inneren Schleife?" - - kind: transfer - text: "Wie erzeugst du ein Rechteck aus zwei Zeilen und sechs Spalten?" -@end-unosim-tutor */ diff --git a/examples/IT06/it06-05-verschachtelte-schleifen.ino b/examples/IT06/it06-05-verschachtelte-schleifen.ino new file mode 100644 index 0000000..2a149e8 --- /dev/null +++ b/examples/IT06/it06-05-verschachtelte-schleifen.ino @@ -0,0 +1,70 @@ +/* + * IT06.5 – Zwei Schleifen verschachteln + * + * Lernziel: + * Eine innere Schleife für jeden Durchlauf einer äußeren Schleife ausführen, + * um Zeilen und Spalten auszugeben. + * + * Bezug zu den Unterlagen: + * IT06, Folien „Doppelschleife“ und „Verschachtelte Schleifen“; + * IT03 Transferaufgabe „Spielfeld 3x3“. + * + * Beobachtung: + * Ein Spielfeld mit 3 Zeilen und 3 Spalten, nummeriert von 1 bis 9. + */ + +const byte ZEILEN = 3; +const byte SPALTEN = 3; + +void setup() { + Serial.begin(115200); + + for (byte zeile = 0; zeile < ZEILEN; zeile++) { + for (byte spalte = 0; spalte < SPALTEN; spalte++) { + byte feld = zeile * SPALTEN + spalte + 1; + Serial.print("["); + Serial.print(feld); + Serial.print("]"); + } + Serial.println(); + } +} + +void loop() { +} + +/* + * Experimente: + * 1. Erzeuge ein Spielfeld mit 4 Zeilen und 5 Spalten. + * 2. Gib statt der Feldnummer die Zeile und die Spalte aus, z. B. [1,2]. + * 3. Verschiebe Serial.println(); in die innere Schleife. + * + * Lernfragen: + * 1. Wie oft wird die innere Schleife insgesamt durchlaufen? + * 2. Welche Schleife bestimmt die Anzahl der Zeilen? + * 3. Wie entsteht aus zeile und spalte die Feldnummer? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen zwei Schleifen verschachteln und ihren Ablauf beschreiben können." +afterFocus: free +focus: + - id: durchlaeufe + title: "Durchläufe" + objective: "Die Zahl der Durchläufe bestimmen und die Rolle beider Schleifen erklären." + questions: + - kind: prediction + text: "Wie oft wird der Block der inneren Schleife insgesamt ausgeführt?" + - kind: concept + text: "Welche der beiden Schleifen bestimmt die Anzahl der Zeilen, die Schleife mit `zeile` oder die mit `spalte`?" + - id: feldnummer + title: "Feldnummer und Zeilenumbruch" + objective: "Die Berechnung der Feldnummer und die Stellung des Zeilenumbruchs begründen." + questions: + - kind: concept + text: "Wie entsteht `feld` aus `zeile` und `spalte`?" + - kind: prediction + text: "Wie sieht die Ausgabe aus, wenn `Serial.println()` in die innere Schleife verschoben wird?" +@end-unosim-tutor */ diff --git a/examples/IT07/it07-01-funktion-ohne-rueckgabe.ino b/examples/IT07/it07-01-funktion-ohne-rueckgabe.ino index 0742618..f64c3c7 100644 --- a/examples/IT07/it07-01-funktion-ohne-rueckgabe.ino +++ b/examples/IT07/it07-01-funktion-ohne-rueckgabe.ino @@ -1,41 +1,88 @@ /* - * IT07.1 - Eine Funktion ohne Rueckgabe schreiben + * IT07.1 – Wiederkehrende Abläufe in Funktionen bündeln * * Lernziel: - * Wiederverwendbare Anweisungen in einer void-Funktion zusammenfassen. + * Wiederholte Anweisungen in void-Funktionen zusammenfassen und diese + * mehrfach aufrufen. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Bedarf“, „Funktion als UML-Aktivitätsdiagramm“ (dit, dah, + * Buchstaben morsen) und „Funktion“ (Syntax); Morse-Übung. * * Beobachtung: - * Ein Funktionsaufruf erzeugt eine vollstaendige Begruessung. + * Die LED an Pin 13 morst SOS. Gleichzeitig erscheint im Monitor „... --- ...“. */ -void begruessungAusgeben() { - Serial.println("Hallo!"); - Serial.println("Willkommen bei UnoSim."); +const byte PIN_LED = 13; +const int DIT = 200; // ms +const int DAH = 3 * DIT; +const int PAUSE_SYMBOL = DIT; +const int PAUSE_BUCHSTABE = 3 * DIT; + +void signalAusgeben(int dauer) { + digitalWrite(PIN_LED, HIGH); + delay(dauer); + digitalWrite(PIN_LED, LOW); + delay(PAUSE_SYMBOL); +} + +void ditAusgeben() { + Serial.print("."); + signalAusgeben(DIT); +} + +void dahAusgeben() { + Serial.print("-"); + signalAusgeben(DAH); +} + +void morseS() { + ditAusgeben(); + ditAusgeben(); + ditAusgeben(); + Serial.print(" "); + delay(PAUSE_BUCHSTABE); +} + +void morseO() { + dahAusgeben(); + dahAusgeben(); + dahAusgeben(); + Serial.print(" "); + delay(PAUSE_BUCHSTABE); } void setup() { Serial.begin(115200); - begruessungAusgeben(); + pinMode(PIN_LED, OUTPUT); } void loop() { + morseS(); + morseO(); + morseS(); + Serial.println(); + delay(2000); } /* * Experimente: - * 1. Rufe begruessungAusgeben() zweimal auf. - * 2. Ergaenze in der Funktion eine dritte Textzeile. + * 1. Ändere DIT auf 100. An wie vielen Stellen musst du das Programm ändern? + * 2. Schreibe eine Funktion morseH() (vier dit) und morse HO. + * 3. Rufe in loop() statt morseO() dreimal dahAusgeben() auf. Was fehlt dann? * * Lernfragen: - * 1. Wann werden die Anweisungen in der Funktion ausgefuehrt? - * 2. Welche Bedeutung hat der Rueckgabetyp void? - * 3. Welcher Teil ist Funktionsdefinition und welcher Funktionsaufruf? + * 1. Wann werden die Anweisungen in einer Funktion ausgeführt? + * 2. Welche Bedeutung hat der Rückgabetyp void? + * 3. Welchen Vorteil hat es, dass ditAusgeben() und dahAusgeben() dieselbe + * Funktion signalAusgeben() verwenden? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen eine void-Funktion definieren und aufrufen können." + - "Die Studierenden sollen wiederkehrende Abläufe in Funktionen ohne Rückgabe bündeln können." + - "Sie sollen Funktionsdefinition, Funktionsaufruf und Parameter unterscheiden können." afterFocus: free focus: - id: definition-und-aufruf @@ -43,13 +90,17 @@ focus: objective: "Definition und Aufruf unterscheiden und erklären, wann der Funktionskörper läuft." questions: - kind: concept - text: "Welcher Teil ist die Definition und welcher der Aufruf von `begruessungAusgeben`?" + text: "Welcher Teil ist die Definition und welcher der Aufruf von `morseS`?" - kind: concept - text: "Wann werden die Anweisungen in der Funktion ausgeführt?" - - id: rueckgabetyp - title: "Rückgabetyp" - objective: "Die Bedeutung von `void` erklären." + text: "Welche Bedeutung hat der Rückgabetyp `void` bei `ditAusgeben`?" + - id: parameter + title: "Parameter" + objective: "Die Aufgabe des Parameters `dauer` erklären." questions: - kind: concept - text: "Welche Bedeutung hat der Rückgabetyp `void`?" + text: "Welche Aufgabe hat der Parameter `dauer` in `signalAusgeben`?" + - kind: prediction + text: "Wie ändert sich der Ablauf, wenn `DIT` auf 100 gesetzt wird?" + - kind: transfer + text: "Wie würdest du eine Funktion für den Buchstaben H schreiben?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-02-funktion-mit-rueckgabe.ino b/examples/IT07/it07-02-funktion-mit-rueckgabe.ino index 4bb4ce3..7df2188 100644 --- a/examples/IT07/it07-02-funktion-mit-rueckgabe.ino +++ b/examples/IT07/it07-02-funktion-mit-rueckgabe.ino @@ -1,58 +1,77 @@ /* - * IT07.2 - Einen Wert aus einer Funktion zurueckgeben + * IT07.2 – Einen Wert aus einer Funktion zurückgeben * * Lernziel: - * Einer Funktion einen Parameter uebergeben und ihr Ergebnis mit return nutzen. + * Einer Funktion Parameter übergeben, ihr Ergebnis mit return zurückgeben + * und einen Funktionsprototyp lesen. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Funktionen“ (Was geht rein? Was kommt zurück?), + * „Beispiel Funktion zur Berechnung des Mittelwertes zweier Zahlen“ und + * „Funktionsprototyp“. * * Beobachtung: - * Die Funktion berechnet das Quadrat der uebergebenen Zahl. + * Der Mittelwert von 4 und 8 ist 6.00, der von 4 und 7 ist 5.50. */ -int quadrat(int zahl) { - return zahl * zahl; -} +float mittelwert(int wert1, int wert2); // Funktionsprototyp: nur die Schnittstelle void setup() { Serial.begin(115200); - int ergebnis = quadrat(6); - Serial.println(ergebnis); + float mw = mittelwert(4, 8); + Serial.print("Mittelwert von 4 und 8: "); + Serial.println(mw, 2); + + mw = mittelwert(4, 7); + Serial.print("Mittelwert von 4 und 7: "); + Serial.println(mw, 2); } void loop() { } +float mittelwert(int wert1, int wert2) { + float ergebnis = (wert1 + wert2) / 2.0; + return ergebnis; +} + /* * Experimente: - * 1. Uebergib der Funktion die Zahl 9. - * 2. Verwende den Aufruf direkt in Serial.println(). + * 1. Ersetze 2.0 durch 2. Welcher Mittelwert ergibt sich jetzt für 4 und 7? + * 2. Verwende den Aufruf direkt in Serial.println(mittelwert(10, 5), 2);. + * 3. Entferne den Funktionsprototyp. Die Arduino-Umgebung erzeugt Prototypen + * automatisch, deshalb compiliert das Programm trotzdem. * * Lernfragen: * 1. Was geht in die Funktion hinein und was kommt heraus? - * 2. Welchen Datentyp besitzt der Rueckgabewert? - * 3. Was bewirkt die Anweisung return? + * 2. Was bewirkt die Anweisung return? + * 3. Warum ist (wert1 + wert2) / 2 eine ganzzahlige Division? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen Parameter und Rückgabewert einer Funktion unterscheiden können." + - "Die Studierenden sollen eine Funktion mit Parametern und Rückgabewert schreiben und aufrufen können." + - "Sie sollen erklären können, warum die Division im Mittelwert mit 2.0 gerechnet wird." afterFocus: free focus: - - id: eingabe-und-ergebnis - title: "Eingabe und Ergebnis" - objective: "Parameter und Rückgabewert benennen und den Rückgabetyp erklären." + - id: schnittstelle + title: "Schnittstelle der Funktion" + objective: "Parameter, Rückgabetyp und Prototyp benennen." questions: - kind: concept - text: "Was geht in `quadrat` hinein, und was kommt heraus?" + text: "Was geht in `mittelwert` hinein, und was kommt heraus?" - kind: concept - text: "Welchen Datentyp besitzt der Rückgabewert?" - - id: return - title: "Die Anweisung return" - objective: "Die Wirkung von `return` erklären und ein Ergebnis vorhersagen." + text: "Wozu dient die Zeile mit dem Prototyp von `mittelwert` vor `setup`?" + - id: rueckgabe + title: "Rückgabe und Division" + objective: "Die Wirkung von `return` erklären und den Einfluss von `2.0` vorhersagen." questions: - kind: concept - text: "Was bewirkt die Anweisung `return`?" + text: "Was bewirkt `return ergebnis`?" - kind: prediction - text: "Welcher Wert steht nach `quadrat(6)` in `ergebnis`?" + text: "Welcher Mittelwert ergibt sich für 4 und 7, wenn `2.0` durch 2 ersetzt wird?" + - kind: transfer + text: "Wie würdest du eine Funktion schreiben, die den Mittelwert von drei Zahlen zurückgibt?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-03-call-by-value.ino b/examples/IT07/it07-03-call-by-value.ino index 4eeaf1b..d9446af 100644 --- a/examples/IT07/it07-03-call-by-value.ino +++ b/examples/IT07/it07-03-call-by-value.ino @@ -1,25 +1,37 @@ /* - * IT07.3 - Einen Parameter als Wert uebergeben + * IT07.3 – Einen Parameter als Wert übergeben (Call-by-value) * * Lernziel: - * Beobachten, dass Call-by-value in der Funktion mit einer Kopie arbeitet. + * Beobachten, dass die Funktion mit einer Kopie arbeitet und eine Änderung + * nur über den Rückgabewert nach außen gelangt. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Varianten der Parameterübergabe“ (Call-by-value, func1) + * und „Übrigens: aNum += 100“. * * Beobachtung: - * Die Zahl wird in der Funktion veraendert, ausserhalb bleibt sie unveraendert. + * zaehler bleibt nach dem ersten Aufruf 0. Erst wenn der Rückgabewert + * zugewiesen wird, steht in zaehler 100. */ -void erhoehen(int zahl) { - zahl = zahl + 1; - Serial.print("In der Funktion: "); - Serial.println(zahl); +int plusHundert(int zahl) { + zahl += 100; // dasselbe wie zahl = zahl + 100; + return zahl; } void setup() { Serial.begin(115200); - int zaehler = 10; - erhoehen(zaehler); - Serial.print("Nach dem Aufruf: "); + int zaehler = 0; + Serial.print("Start: "); + Serial.println(zaehler); + + plusHundert(zaehler); + Serial.print("Rueckgabe nicht verwendet: "); + Serial.println(zaehler); + + zaehler = plusHundert(zaehler); + Serial.print("Rueckgabe zugewiesen: "); Serial.println(zaehler); } @@ -28,13 +40,14 @@ void loop() { /* * Experimente: - * 1. Erhoehe zahl in der Funktion um 100. - * 2. Rufe die Funktion zweimal mit demselben zaehler auf. + * 1. Gib in plusHundert() den Wert von zahl vor dem return aus. + * 2. Rufe zaehler = plusHundert(zaehler); zweimal hintereinander auf. + * 3. Benenne den Parameter zahl in zaehler um. Ändert sich das Verhalten? * * Lernfragen: - * 1. Welcher Wert wird beim Aufruf kopiert? - * 2. Warum bleibt zaehler ausserhalb der Funktion gleich? - * 3. In welchem Bereich ist der Parameter zahl gueltig? + * 1. Welcher Wert wird beim Aufruf in den Parameter zahl kopiert? + * 2. Warum bleibt zaehler nach dem ersten Aufruf 0? + * 3. In welchem Bereich ist der Parameter zahl gültig? */ /* @unosim-tutor @@ -44,17 +57,19 @@ learningObjectives: afterFocus: free focus: - id: kopie - title: "Der Wert wird kopiert" + title: "Die Kopie" objective: "Erklären, was beim Aufruf kopiert wird und warum das Original unverändert bleibt." questions: - kind: concept - text: "Welcher Wert wird beim Aufruf von `erhoehen` kopiert?" + text: "Welcher Wert wird beim Aufruf von `plusHundert` in den Parameter `zahl` kopiert?" - kind: concept - text: "Warum bleibt `zaehler` außerhalb der Funktion unverändert?" - - id: gueltigkeit - title: "Gültigkeit des Parameters" - objective: "Den Gültigkeitsbereich des Parameters bestimmen." + text: "Warum bleibt `zaehler` nach dem ersten Aufruf von `plusHundert` unverändert?" + - id: rueckgabe + title: "Ergebnis nach außen geben" + objective: "Den Weg des Ergebnisses über den Rückgabewert beschreiben." questions: + - kind: prediction + text: "Welchen Wert hat `zaehler`, wenn `zaehler = plusHundert(zaehler)` zweimal hintereinander ausgeführt wird?" - kind: concept - text: "In welchem Bereich ist der Parameter `zahl` gültig?" + text: "Ändert sich das Verhalten, wenn der Parameter `zahl` ebenfalls `zaehler` heißt, und warum?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-04-call-by-reference.ino b/examples/IT07/it07-04-call-by-reference.ino index a172363..ede8524 100644 --- a/examples/IT07/it07-04-call-by-reference.ino +++ b/examples/IT07/it07-04-call-by-reference.ino @@ -1,22 +1,30 @@ /* - * IT07.4 - Die Adresse einer Variablen uebergeben + * IT07.4 – Die Adresse einer Variablen übergeben (Call-by-reference) * * Lernziel: - * Eine Variable ueber einen Zeiger in einer Funktion veraendern. + * Eine Variable über ihre Adresse (Zeiger) in einer Funktion verändern. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Varianten der Parameterübergabe“ (Call-by-reference, func2): + * * im Parameter, * beim Schreiben, & beim Aufruf. * * Beobachtung: - * Der geaenderte Wert bleibt nach dem Funktionsaufruf erhalten. + * Nach dem Aufruf steht in zaehler 50. Die Änderung bleibt erhalten. */ -void erhoehen(int *zahl) { - *zahl = *zahl + 1; +void plusFuenfzig(int *zahl) { + *zahl += 50; // den Wert an der Adresse ändern, nicht die Adresse } void setup() { Serial.begin(115200); - int zaehler = 10; - erhoehen(&zaehler); + int zaehler = 0; + Serial.print("Vor dem Aufruf: "); + Serial.println(zaehler); + + plusFuenfzig(&zaehler); + Serial.print("Nach dem Aufruf: "); Serial.println(zaehler); } @@ -25,19 +33,21 @@ void loop() { /* * Experimente: - * 1. Erhoehe den Wert in der Funktion um 5. - * 2. Entferne beim Aufruf den Adressoperator & und lies die Fehlermeldung. + * 1. Rufe plusFuenfzig(&zaehler); dreimal auf. + * 2. Entferne beim Aufruf das & und lies die Fehlermeldung. + * 3. Entferne in der Funktion das * vor zahl und lies die Fehlermeldung. * * Lernfragen: * 1. Welche Bedeutung hat & beim Funktionsaufruf? - * 2. Welche Bedeutung hat * vor zahl in der Funktion? - * 3. Warum bleibt die Aenderung nach dem Funktionsaufruf erhalten? + * 2. Welche Bedeutung hat * in der Parameterliste, welche in *zahl += 50? + * 3. Warum bleibt die Änderung nach dem Funktionsaufruf erhalten? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - "Die Studierenden sollen eine Variable über einen Zeiger in einer Funktion verändern können." + - "Sie sollen Call-by-value und Call-by-reference unterscheiden können." afterFocus: free focus: - id: adresse-und-zeiger @@ -45,13 +55,15 @@ focus: objective: "Die Bedeutung von `&` beim Aufruf und `*` in der Funktion erklären." questions: - kind: concept - text: "Welche Bedeutung hat `&` beim Aufruf `erhoehen(&zaehler)`?" + text: "Welche Bedeutung hat `&` beim Aufruf `plusFuenfzig(&zaehler)`?" - kind: concept - text: "Welche Bedeutung hat `*` vor `zahl` in der Funktion?" + text: "Welche Bedeutung hat `*` in `*zahl += 50`?" - id: wirkung - title: "Wirkung außerhalb der Funktion" - objective: "Begründen, warum die Änderung erhalten bleibt." + title: "Wirkung auf das Original" + objective: "Begründen, warum die Änderung erhalten bleibt, und mit Call-by-value vergleichen." questions: - kind: concept - text: "Warum bleibt die Änderung nach dem Funktionsaufruf erhalten?" + text: "Warum hat `zaehler` nach dem Aufruf von `plusFuenfzig` einen anderen Wert?" + - kind: prediction + text: "Welchen Wert hat `zaehler`, wenn `plusFuenfzig(&zaehler)` dreimal aufgerufen wird?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-05-eindimensionales-array.ino b/examples/IT07/it07-05-eindimensionales-array.ino index 65fe72b..0873f59 100644 --- a/examples/IT07/it07-05-eindimensionales-array.ino +++ b/examples/IT07/it07-05-eindimensionales-array.ino @@ -1,21 +1,39 @@ /* - * IT07.5 - Gleichartige Werte in einem Array speichern + * IT07.5 – Gleichartige Werte in einem Array speichern * * Lernziel: - * Ein eindimensionales Array deklarieren und ueber seinen Index auslesen. + * Ein Array direkt initialisieren oder mit einer Schleife füllen und über + * den Index auf seine Elemente zugreifen. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Array“, „Achtung: Der Index beginnt bei 0!“, „Direkte + * Initialisierung“ und „Hier wird das Array mit einer Schleife gefüllt“. * * Beobachtung: - * Die Schleife gibt alle vier gespeicherten Messwerte aus. + * werte[2] ist 7. Danach erscheinen vier Zeitpunkte im Abstand von etwa 200 ms. */ -const byte ANZAHL_WERTE = 4; -int messwerte[ANZAHL_WERTE] = {120, 135, 128, 142}; +byte werte[] = {23, 42, 7, 3}; + +const byte ANZAHL = 4; +unsigned long zeitpunkte[ANZAHL]; void setup() { Serial.begin(115200); - for (byte i = 0; i < ANZAHL_WERTE; i++) { - Serial.println(messwerte[i]); + Serial.print("werte[2] = "); + Serial.println(werte[2]); + + for (byte i = 0; i < ANZAHL; i++) { + zeitpunkte[i] = millis(); + delay(200); + } + + for (byte i = 0; i < ANZAHL; i++) { + Serial.print("zeitpunkte["); + Serial.print(i); + Serial.print("] = "); + Serial.println(zeitpunkte[i]); } } @@ -24,19 +42,21 @@ void loop() { /* * Experimente: - * 1. Aendere den Wert des Elements mit dem Index 2. - * 2. Fuege ein fuenftes Element hinzu. + * 1. Setze werte[0] = 99; und gib das Element danach aus. + * 2. Ändere ANZAHL auf 6. An wie vielen Stellen musst du das Programm ändern? + * 3. Ändere die zweite Schleifenbedingung in i <= ANZAHL. Was wird zusätzlich + * ausgegeben? (Der Wert ist zufällig – es wird außerhalb des Arrays gelesen.) * * Lernfragen: - * 1. Mit welchem Index beginnt ein Array? - * 2. Welchen Wert besitzt messwerte[2] zu Beginn? - * 3. Warum muss die Schleifenbedingung i < ANZAHL_WERTE lauten? + * 1. Mit welchem Index beginnt ein Array, mit welchem endet es? + * 2. Warum muss die Schleifenbedingung i < ANZAHL lauten? + * 3. Warum darf bei werte die Größe in den eckigen Klammern fehlen? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen ein eindimensionales Array deklarieren und über den Index auslesen können." + - "Die Studierenden sollen ein eindimensionales Array anlegen, füllen und über den Index auslesen können." afterFocus: free focus: - id: index @@ -44,21 +64,15 @@ focus: objective: "Den Startindex nennen und ein Element über seinen Index bestimmen." questions: - kind: recall - text: "Mit welchem Index beginnt das Array `messwerte`?" - - kind: recall - text: "Welchen Wert besitzt das Element von `messwerte` mit dem Index 2 zu Beginn?" - - id: schleife-und-grenze - title: "Schleife und Obergrenze" - objective: "Die Schleifenbedingung über das Array begründen." + text: "Mit welchem Index beginnt das Array `werte`, und mit welchem endet es?" + - kind: application + text: "Welchen Wert liefert `werte[2]`, und warum ist das nicht das zweite Element?" + - id: schleife + title: "Array und Schleife" + objective: "Die Schleifenbedingung über das Array begründen und die Konstante `ANZAHL` nutzen." questions: - kind: concept - text: "Warum muss die Bedingung `i < ANZAHL_WERTE` lauten und darf nicht „kleiner oder gleich“ sein?" - - kind: prediction - text: "Was geschähe, wenn `ANZAHL_WERTE` größer als die Zahl der gespeicherten Werte wäre?" - - id: elementtyp - title: "Der Elementtyp" - objective: "Die Rolle des Elementtyps erklären." - questions: + text: "Warum muss die Bedingung `i < ANZAHL` lauten und darf nicht kleiner oder gleich sein?" - kind: concept - text: "Welche Rolle spielt der Elementtyp `int` des Arrays `messwerte`?" + text: "Welchen Vorteil hat es, dass `ANZAHL` sowohl die Größe von `zeitpunkte` als auch die Schleifengrenze festlegt?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-06-zweidimensionales-array.ino b/examples/IT07/it07-06-zweidimensionales-array.ino index c1525b2..8cac7bf 100644 --- a/examples/IT07/it07-06-zweidimensionales-array.ino +++ b/examples/IT07/it07-06-zweidimensionales-array.ino @@ -1,18 +1,26 @@ /* - * IT07.6 - Werte in Zeilen und Spalten speichern + * IT07.6 – Ein Tastenfeld als zweidimensionales Array * * Lernziel: - * Ein zweidimensionales Array als Tabelle verwenden. + * Werte in Zeilen und Spalten speichern und mit zwei Indizes auslesen. + * + * Bezug zu den Unterlagen: + * IT07, Folien „2-dimensionales Array“ und Transferaufgabe „Tastenfeld“ + * (nicht verwendete Felder enthalten -1). * * Beobachtung: - * Zwei verschachtelte Schleifen geben eine Tabelle mit zwei Zeilen aus. + * Das Tastenfeld erscheint in 4 Zeilen. Nicht belegte Felder sind leer. + * Darunter steht die Taste in Zeile 3, Spalte 1: 0. */ -const byte ZEILEN = 2; +const byte ZEILEN = 4; const byte SPALTEN = 3; -int tabelle[ZEILEN][SPALTEN] = { - {1, 2, 3}, - {4, 5, 6} + +int tastenfeld[ZEILEN][SPALTEN] = { + { 9, 8, 7 }, + { 6, 5, 4 }, + { 3, 2, 1 }, + { -1, 0, -1 } }; void setup() { @@ -20,11 +28,18 @@ void setup() { for (byte zeile = 0; zeile < ZEILEN; zeile++) { for (byte spalte = 0; spalte < SPALTEN; spalte++) { - Serial.print(tabelle[zeile][spalte]); - Serial.print(" "); + if (tastenfeld[zeile][spalte] == -1) { + Serial.print(" "); + } else { + Serial.print(tastenfeld[zeile][spalte]); + } + Serial.print("\t"); } Serial.println(); } + + Serial.print("Zeile 3, Spalte 1: "); + Serial.println(tastenfeld[3][1]); } void loop() { @@ -32,19 +47,21 @@ void loop() { /* * Experimente: - * 1. Aendere den Wert in der zweiten Zeile und dritten Spalte. - * 2. Erweitere die Tabelle um eine dritte Zeile. + * 1. Gib die Taste 5 über ihre beiden Indizes aus. + * 2. Gib das Feld spaltenweise aus: Vertausche dazu die beiden Schleifen. + * 3. Transfer der Folie: Speichere zusätzlich zu jeder Taste die Anzahl der + * Betätigungen (drei Indizes). * * Lernfragen: - * 1. Welche zwei Indizes werden fuer ein Element benoetigt? - * 2. Welchen Wert liefert tabelle[1][2]? - * 3. Welche Schleife laeuft ueber die Spalten? + * 1. Welche zwei Indizes werden für ein Element benötigt? + * 2. Welche Schleife läuft über die Spalten? + * 3. Wozu dient der Wert -1 in der letzten Zeile? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen ein zweidimensionales Array als Tabelle verwenden können." + - "Die Studierenden sollen ein zweidimensionales Array anlegen und mit zwei Indizes auslesen können." afterFocus: free focus: - id: zwei-indizes @@ -52,15 +69,17 @@ focus: objective: "Die zwei Indizes eines Elements erklären und ein Element bestimmen." questions: - kind: concept - text: "Welche zwei Indizes werden für ein Element von `tabelle` benötigt?" - - kind: prediction - text: "Welchen Wert hat das Element von `tabelle` in der zweiten Zeile und dritten Spalte?" - - id: schleifen - title: "Die Schleifen" - objective: "Die Aufgabe beider Schleifen zuordnen." + text: "Wofür steht der erste und wofür der zweite Index von `tastenfeld`?" + - kind: application + text: "Mit welchen Indizes erreichst du in `tastenfeld` die Taste 5?" + - id: ausgabe + title: "Ausgabe mit zwei Schleifen" + objective: "Die Aufgabe der Schleifen und des Sonderwerts erklären." questions: - kind: concept - text: "Welche Schleife läuft über die Spalten?" + text: "Welche der beiden Schleifen läuft über die Spalten?" + - kind: concept + text: "Wozu dient der Wert -1 in `tastenfeld`, und wie behandelt die Ausgabe ihn?" - kind: transfer - text: "Wie würdest du die Tabelle um eine dritte Zeile erweitern?" + text: "Wie würdest du zu jeder Taste zusätzlich die Anzahl ihrer Betätigungen speichern?" @end-unosim-tutor */ diff --git a/examples/IT07/it07-07-array-an-funktion.ino b/examples/IT07/it07-07-array-an-funktion.ino new file mode 100644 index 0000000..044a2fe --- /dev/null +++ b/examples/IT07/it07-07-array-an-funktion.ino @@ -0,0 +1,91 @@ +/* + * IT07.7 – Ein Array an eine Funktion übergeben + * + * Lernziel: + * Ein Array an Funktionen übergeben, die es füllen und auswerten, und + * erklären, warum dabei das Original verändert wird. + * + * Bezug zu den Unterlagen: + * IT07, Folien „Arrays und Funktionen“ (immer Call-by-reference, ohne &) + * und Übung Datenlogger (Mittelwert über 5 Messwerte). + * + * Beobachtung: + * Jede Sekunde werden 5 Messwerte von A0 erfasst, ausgegeben und gemittelt. + */ + +const byte PIN_SENSOR = A0; +const byte ANZAHL = 5; + +void messwerteErfassen(unsigned int messwerte[]) { + for (byte i = 0; i < ANZAHL; i++) { + messwerte[i] = analogRead(PIN_SENSOR); + delay(100); + } +} + +float mittelwertBerechnen(unsigned int messwerte[]) { + float summe = 0; + for (byte i = 0; i < ANZAHL; i++) { + summe += messwerte[i]; + } + return summe / ANZAHL; +} + +void setup() { + Serial.begin(115200); +} + +void loop() { + unsigned int messwerte[ANZAHL]; + + messwerteErfassen(messwerte); + + for (byte i = 0; i < ANZAHL; i++) { + Serial.print(messwerte[i]); + Serial.print(" "); + } + Serial.print("-> Mittelwert: "); + Serial.println(mittelwertBerechnen(messwerte), 1); + + delay(1000); +} + +/* + * Experimente: + * 1. Verändere A0 im Simulator, während gemessen wird. + * 2. Erhöhe ANZAHL auf 10. + * 3. Schreibe eine Funktion maximumBestimmen(), die den größten Messwert + * zurückgibt. + * + * Lernfragen: + * 1. Warum steht beim Aufruf messwerteErfassen(messwerte) kein &? + * 2. Warum enthält messwerte nach dem Aufruf die gemessenen Werte? + * 3. Warum wird summe als float deklariert? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen Arrays an Funktionen übergeben und dort füllen und auswerten können." + - "Sie sollen erklären können, warum Arrays immer als Referenz übergeben werden." +afterFocus: free +focus: + - id: uebergabe + title: "Übergabe eines Arrays" + objective: "Erklären, warum die Funktion das Original-Array verändert." + questions: + - kind: concept + text: "Warum enthält `messwerte` in `loop` nach dem Aufruf von `messwerteErfassen` die gemessenen Werte?" + - kind: concept + text: "Warum steht beim Aufruf `messwerteErfassen(messwerte)` kein Adressoperator?" + - id: auswertung + title: "Auswertung" + objective: "Die Berechnung des Mittelwerts erklären und erweitern." + questions: + - kind: concept + text: "Warum wird `summe` als `float` deklariert?" + - kind: prediction + text: "Welcher Mittelwert erscheint, wenn vier Messwerte 100 sind und einer 600 ist?" + - kind: transfer + text: "Wie würdest du eine Funktion schreiben, die den größten Wert aus `messwerte` zurückgibt?" +@end-unosim-tutor */ diff --git a/examples/IT08/it08-01-struct.ino b/examples/IT08/it08-01-struct.ino index eee0cd4..31504e3 100644 --- a/examples/IT08/it08-01-struct.ino +++ b/examples/IT08/it08-01-struct.ino @@ -1,13 +1,20 @@ /* - * IT08.1 - Unterschiedliche Daten zusammenfassen + * IT08.1 – Unterschiedliche Daten in einer Struktur zusammenfassen * * Lernziel: - * Mit struct einen eigenen zusammengesetzten Datentyp definieren. + * Mit struct einen eigenen Datentyp aus Zeitstempel, Messstelle und + * Messwert definieren und mit dem Punktoperator auf die Felder zugreifen. + * + * Bezug zu den Unterlagen: + * IT08, Folien „Bedarf“, „struct – Deklaration der Struktur“ und + * „struct – Deklaration einer Variablen der Struktur“. * * Beobachtung: - * Zeit, Messstelle und Messwert werden als zusammengehoeriger Datensatz ausgegeben. + * Jede Sekunde erscheint ein Datensatz aus Zeit, Messstelle und Spannung an A0. */ +const byte PIN_SENSOR = A0; + struct Messung { unsigned long zeit; byte messstelle; @@ -16,53 +23,62 @@ struct Messung { void setup() { Serial.begin(115200); +} - Messung messung; +void loop() { + struct Messung messung; messung.zeit = millis(); messung.messstelle = 1; - messung.messwert = 23.5; + messung.messwert = analogRead(PIN_SENSOR) / 1024.0 * 5.0; + Serial.print("Zeit: "); Serial.print(messung.zeit); - Serial.print(" ms, Stelle "); + Serial.print(" ms\tStelle: "); Serial.print(messung.messstelle); - Serial.print(": "); - Serial.println(messung.messwert, 1); -} + Serial.print("\tWert: "); + Serial.print(messung.messwert, 2); + Serial.println(" V"); -void loop() { + delay(1000); } /* * Experimente: - * 1. Aendere Messstelle und Messwert. - * 2. Ergaenze die Struktur um ein bool-Feld gueltig. + * 1. Ergänze das Feld bool gueltig und setze es auf true, wenn der Messwert + * größer als 0 V ist. + * 2. Lege eine zweite Variable struct Messung messung2 für A1 an. + * 3. Versuche, messung als Ganzes mit Serial.println(messung); auszugeben, + * und lies die Fehlermeldung. * * Lernfragen: * 1. Welche Datentypen fasst Messung zusammen? - * 2. Welche Aufgabe hat der Punktoperator? - * 3. Ist Messung ein Datentyp oder bereits eine Variable? + * 2. Ist Messung ein Datentyp oder bereits eine Variable? + * 3. Welche Aufgabe hat der Punktoperator? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen mit struct einen zusammengesetzten Datentyp definieren können." + - "Die Studierenden sollen eine Struktur definieren und auf ihre Felder zugreifen können." + - "Sie sollen zwischen dem Strukturtyp und einer Variablen dieses Typs unterscheiden können." afterFocus: free focus: - - id: datentyp-oder-variable - title: "Datentyp oder Variable" + - id: typ-und-variable + title: "Strukturtyp und Variable" objective: "Zwischen Strukturtyp und Variable unterscheiden und die enthaltenen Datentypen benennen." questions: - kind: concept text: "Ist `Messung` ein Datentyp oder bereits eine Variable?" - - kind: concept + - kind: recall text: "Welche Datentypen fasst `Messung` zusammen?" - - id: feldzugriff + - id: punktoperator title: "Zugriff auf Felder" - objective: "Den Punktoperator erklären und den Typ eines Feldes begründen." + objective: "Den Punktoperator erklären und die Wahl der Felddatentypen begründen." questions: - kind: concept - text: "Welche Aufgabe hat der Punktoperator in `messung.messwert`?" + text: "Welche Aufgabe hat der Punkt in `messung.messwert`?" - kind: application text: "Warum ist für das Feld `zeit` der Typ `unsigned long` passend?" + - kind: transfer + text: "Welches Feld würdest du ergänzen, um zu speichern, ob ein Messwert gültig ist?" @end-unosim-tutor */ diff --git a/examples/IT08/it08-02-array-aus-structs.ino b/examples/IT08/it08-02-array-aus-structs.ino deleted file mode 100644 index 7bfa9bd..0000000 --- a/examples/IT08/it08-02-array-aus-structs.ino +++ /dev/null @@ -1,67 +0,0 @@ -/* - * IT08.2 - Mehrere strukturierte Datensaetze speichern - * - * Lernziel: - * Ein Array verwenden, dessen Elemente Strukturen sind. - * - * Beobachtung: - * Drei Messungen werden nacheinander aus dem Array ausgegeben. - */ - -struct Messung { - byte messstelle; - float messwert; -}; - -const byte ANZAHL_MESSUNGEN = 3; -Messung messungen[ANZAHL_MESSUNGEN] = { - {1, 20.5}, - {2, 21.0}, - {1, 20.8} -}; - -void setup() { - Serial.begin(115200); - - for (byte i = 0; i < ANZAHL_MESSUNGEN; i++) { - Serial.print(messungen[i].messstelle); - Serial.print(": "); - Serial.println(messungen[i].messwert, 1); - } -} - -void loop() { -} - -/* - * Experimente: - * 1. Aendere den Messwert des zweiten Datensatzes. - * 2. Fuege eine vierte Messung hinzu. - * - * Lernfragen: - * 1. Welchen Datentyp besitzt ein einzelnes Arrayelement? - * 2. Was bedeutet messungen[1].messwert? - * 3. Warum koennen die Felder einer Struktur verschiedene Datentypen haben? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen ein Array aus Strukturen verwenden können." -afterFocus: free -focus: - - id: array-aus-structs - title: "Array aus Strukturen" - objective: "Den Typ eines Elements bestimmen und den Zugriff auf ein Feld erklären." - questions: - - kind: concept - text: "Welchen Datentyp besitzt ein einzelnes Element von `messungen`?" - - kind: concept - text: "Was bedeutet es, das Feld `messwert` des Elements mit dem Index 1 von `messungen` anzusprechen?" - - id: gemischte-typen - title: "Gemischte Datentypen" - objective: "Begründen, warum Felder verschiedene Typen haben dürfen." - questions: - - kind: concept - text: "Warum können die Felder einer `struct` verschiedene Datentypen haben?" -@end-unosim-tutor */ diff --git a/examples/IT08/it08-02-struct-uebergeben.ino b/examples/IT08/it08-02-struct-uebergeben.ino new file mode 100644 index 0000000..1a008f5 --- /dev/null +++ b/examples/IT08/it08-02-struct-uebergeben.ino @@ -0,0 +1,88 @@ +/* + * IT08.2 – Eine Struktur als Wert oder als Zeiger übergeben + * + * Lernziel: + * Eine Struktur einmal als Kopie (Punkt) und einmal über ihre Adresse + * (Pfeil ->) an eine Funktion übergeben und die Wirkung vergleichen. + * + * Bezug zu den Unterlagen: + * IT08, Folie „struct – Zugriffsarten“ (Call by value: Punkt, + * Call by reference: Elementverweisoperator ->); IT07 Parameterübergabe. + * + * Beobachtung: + * Nach korrigierenKopie() ist der Messwert noch 20.0, nach + * korrigierenOriginal() ist er 20.5. + */ + +struct Messung { + byte messstelle; + float messwert; +}; + +void korrigierenKopie(struct Messung messung) { + messung.messwert = messung.messwert + 0.5; + Serial.print(" in korrigierenKopie: "); + Serial.println(messung.messwert, 1); +} + +void korrigierenOriginal(struct Messung *messung) { + messung->messwert = messung->messwert + 0.5; + Serial.print(" in korrigierenOriginal: "); + Serial.println(messung->messwert, 1); +} + +void setup() { + Serial.begin(115200); + + struct Messung messung = {1, 20.0}; + + korrigierenKopie(messung); + Serial.print("nach korrigierenKopie: "); + Serial.println(messung.messwert, 1); + + korrigierenOriginal(&messung); + Serial.print("nach korrigierenOriginal: "); + Serial.println(messung.messwert, 1); +} + +void loop() { +} + +/* + * Experimente: + * 1. Rufe korrigierenOriginal(&messung); zweimal auf. + * 2. Schreibe in korrigierenOriginal() messung.messwert statt messung->messwert + * und lies die Fehlermeldung. + * 3. Ändere korrigierenKopie() so, dass sie die korrigierte Struktur mit + * return zurückgibt, und weise das Ergebnis messung zu. + * + * Lernfragen: + * 1. Warum ändert korrigierenKopie() den Messwert in setup() nicht? + * 2. Wann wird der Punkt verwendet, wann der Pfeil ->? + * 3. Warum wird beim Aufruf &messung geschrieben? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Struktur als Kopie und über einen Zeiger übergeben und die Wirkung vergleichen können." + - "Sie sollen den Punktoperator und den Operator -> richtig einsetzen können." +afterFocus: free +focus: + - id: kopie-oder-original + title: "Kopie oder Original" + objective: "Entscheiden, ob eine Funktion die Kopie oder das Original verändert." + questions: + - kind: concept + text: "Warum ändert `korrigierenKopie` den Messwert in `setup` nicht?" + - kind: prediction + text: "Welcher Messwert steht in `setup`, wenn `korrigierenOriginal(&messung)` zweimal aufgerufen wird?" + - id: punkt-und-pfeil + title: "Punkt und Pfeil" + objective: "Den Punktoperator und `->` je nach Übergabeart einsetzen." + questions: + - kind: concept + text: "Wann wird in diesem Sketch der Punkt verwendet und wann `->`?" + - kind: concept + text: "Warum wird beim Aufruf `&messung` geschrieben?" +@end-unosim-tutor */ diff --git a/examples/IT08/it08-03-array-aus-structs.ino b/examples/IT08/it08-03-array-aus-structs.ino new file mode 100644 index 0000000..8310ca1 --- /dev/null +++ b/examples/IT08/it08-03-array-aus-structs.ino @@ -0,0 +1,94 @@ +/* + * IT08.3 – Mehrere Messstellen in einem Array aus Strukturen + * + * Lernziel: + * Ein Array verwenden, dessen Elemente Strukturen sind, und die Elemente + * über Funktionen füllen und ausgeben. + * + * Bezug zu den Unterlagen: + * IT08, Folien „Beispiel 3: Array of struct, struct als Funktionsparameter“, + * „Einschub: Spaghetticode“ und „Beispiel 4: struct als Funktionsparameter“. + * + * Beobachtung: + * Jede Sekunde erscheinen drei Datensätze für A0, A1 und A2. + * Die Messstelle ist die Pinnummer: A0 hat beim Uno die Nummer 14. + */ + +struct Messung { + unsigned long zeit; + byte messstelle; + float messwert; +}; + +const byte ANZAHL_MESSSTELLEN = 3; +const byte MESSSTELLEN[ANZAHL_MESSSTELLEN] = {A0, A1, A2}; +struct Messung messungen[ANZAHL_MESSSTELLEN]; + +void messwertEinlesen(struct Messung *messung, byte pin) { + messung->zeit = millis(); + messung->messstelle = pin; + messung->messwert = analogRead(pin) / 1024.0 * 5.0; +} + +void messwertAusgeben(struct Messung *messung) { + Serial.print("Stelle "); + Serial.print(messung->messstelle); + Serial.print(": "); + Serial.print(messung->messwert, 2); + Serial.print(" V ("); + Serial.print(messung->zeit); + Serial.println(" ms)"); +} + +void setup() { + Serial.begin(115200); +} + +void loop() { + for (byte i = 0; i < ANZAHL_MESSSTELLEN; i++) { + messwertEinlesen(&messungen[i], MESSSTELLEN[i]); + } + for (byte i = 0; i < ANZAHL_MESSSTELLEN; i++) { + messwertAusgeben(&messungen[i]); + } + Serial.println(); + delay(1000); +} + +/* + * Experimente: + * 1. Stelle im Simulator für A0, A1 und A2 unterschiedliche Werte ein. + * 2. Ergänze die Messstelle A3. + * 3. Gib nur den Messwert des zweiten Datensatzes aus: messungen[1].messwert. + * + * Lernfragen: + * 1. Welchen Datentyp besitzt ein einzelnes Element von messungen? + * 2. Was bedeutet &messungen[i] beim Aufruf? + * 3. Warum ist es besser, die Daten als Parameter zu übergeben, statt in den + * Funktionen direkt das globale Array zu verwenden? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen ein Array aus Strukturen anlegen und über Funktionen verarbeiten können." + - "Sie sollen begründen können, warum Daten als Parameter übergeben werden." +afterFocus: free +focus: + - id: elemente + title: "Elemente des Arrays" + objective: "Den Typ eines Elements bestimmen und den Zugriff auf ein Feld erklären." + questions: + - kind: recall + text: "Welchen Datentyp besitzt ein einzelnes Element von `messungen`?" + - kind: concept + text: "Was wird beim Aufruf mit `&messungen[i]` an die Funktion übergeben?" + - id: parameter-statt-global + title: "Parameter statt globaler Zugriff" + objective: "Den Vorteil der Übergabe als Parameter begründen." + questions: + - kind: concept + text: "Warum greifen `messwertEinlesen` und `messwertAusgeben` nicht direkt auf `messungen` zu?" + - kind: transfer + text: "Was musst du ändern, um eine vierte Messstelle zu ergänzen?" +@end-unosim-tutor */ diff --git a/examples/IT08/it08-03-struct-als-parameter.ino b/examples/IT08/it08-03-struct-als-parameter.ino deleted file mode 100644 index 6f6a937..0000000 --- a/examples/IT08/it08-03-struct-als-parameter.ino +++ /dev/null @@ -1,62 +0,0 @@ -/* - * IT08.3 - Eine Struktur an eine Funktion uebergeben - * - * Lernziel: - * Eine Struktur per Referenz an eine Funktion uebergeben. - * - * Beobachtung: - * Die Funktion veraendert den Messwert im urspruenglichen Datensatz. - */ - -struct Messung { - byte messstelle; - float messwert; -}; - -void messwertKorrigieren(struct Messung *messung) { - messung->messwert = messung->messwert + 0.5; -} - -void setup() { - Serial.begin(115200); - - Messung messung = {1, 20.0}; - messwertKorrigieren(&messung); - Serial.println(messung.messwert, 1); -} - -void loop() { -} - -/* - * Experimente: - * 1. Korrigiere den Messwert um -1.0. - * 2. Gib den Wert vor und nach dem Funktionsaufruf aus. - * - * Lernfragen: - * 1. Warum wird beim Aufruf &messung verwendet? - * 2. Welche Bedeutung hat der Operator ->? - * 3. Wird in der Funktion eine Kopie oder das Original veraendert? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen eine Struktur per Zeiger an eine Funktion übergeben können." -afterFocus: free -focus: - - id: zeiger-uebergabe - title: "Übergabe per Zeiger" - objective: "Die Rolle von `&` beim Aufruf und `->` in der Funktion erklären." - questions: - - kind: concept - text: "Warum wird beim Aufruf `&messung` verwendet?" - - kind: concept - text: "Welche Bedeutung hat der Operator `->`?" - - id: kopie-oder-original - title: "Kopie oder Original" - objective: "Entscheiden, ob Kopie oder Original verändert wird." - questions: - - kind: concept - text: "Wird in `messwertKorrigieren` eine Kopie oder das Original verändert?" -@end-unosim-tutor */ diff --git a/examples/IT08/it08-04-funktion-auslagern.ino b/examples/IT08/it08-04-funktion-auslagern.ino deleted file mode 100644 index 7d11342..0000000 --- a/examples/IT08/it08-04-funktion-auslagern.ino +++ /dev/null @@ -1,52 +0,0 @@ -/* - * IT08.4 - Eine Funktion in eine Headerdatei auslagern - * - * Lernziel: - * Eine eigene Headerdatei mit #include in das Hauptprogramm einbinden. - * - * Beobachtung: - * Die ausgelagerte Funktion berechnet weiterhin das erwartete Ergebnis. - */ - -#include "rechnen.h" - -void setup() { - Serial.begin(115200); - Serial.println(addieren(7, 5)); -} - -void loop() { -} - -/* - * Experimente: - * 1. Aendere die beiden an addieren() uebergebenen Werte. - * 2. Ergaenze in rechnen.h eine Funktion subtrahieren(). - * - * Lernfragen: - * 1. Welche Aufgabe hat die Zeile mit #include? - * 2. In welcher Datei ist addieren() implementiert? - * 3. Welchen Vorteil bietet die Trennung bei groesseren Programmen? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen eine Funktion in eine Headerdatei auslagern und einbinden können." -afterFocus: free -focus: - - id: include - title: "Die Include-Zeile" - objective: "Die Aufgabe von `#include` erklären und die Implementierung zuordnen." - questions: - - kind: concept - text: "Welche Aufgabe hat die Zeile mit `#include`?" - - kind: concept - text: "In welcher Datei ist `addieren` implementiert?" - - id: trennung - title: "Trennung von Dateien" - objective: "Den Nutzen der Trennung begründen." - questions: - - kind: concept - text: "Welchen Vorteil bietet die Trennung bei größeren Programmen?" -@end-unosim-tutor */ diff --git a/examples/IT08/it08-04-typedef.ino b/examples/IT08/it08-04-typedef.ino new file mode 100644 index 0000000..77a0641 --- /dev/null +++ b/examples/IT08/it08-04-typedef.ino @@ -0,0 +1,98 @@ +/* + * IT08.4 – Mit typedef einen lesbaren Datentyp festlegen + * + * Lernziel: + * Mit typedef einen Alias für eine Struktur festlegen und Strukturen als + * Parameter und Rückgabewert von Funktionen verwenden. + * + * Bezug zu den Unterlagen: + * IT08, Folie „struct – mit typedef“ und Abschnitt „Beispiel Unit Testing + * für Vektorfunktionen“ (Vektor addieren, Länge berechnen). + * + * Beobachtung: + * (0, 1) + (2, 2) = (2.00, 3.00). Die Länge des Ergebnisses ist 3.61. + */ + +typedef struct vektor { + float x; + float y; +} Vektor; + +Vektor addieren(Vektor v, Vektor s) { + v.x += s.x; + v.y += s.y; + return v; +} + +float laenge(Vektor v) { + return sqrt(v.x * v.x + v.y * v.y); +} + +void ausgeben(Vektor v) { + Serial.print("("); + Serial.print(v.x, 2); + Serial.print(", "); + Serial.print(v.y, 2); + Serial.print(")"); +} + +void setup() { + Serial.begin(115200); + + Vektor a = {0, 1}; + Vektor b = {2, 2}; + Vektor summe = addieren(a, b); + + ausgeben(a); + Serial.print(" + "); + ausgeben(b); + Serial.print(" = "); + ausgeben(summe); + Serial.println(); + + Serial.print("Laenge: "); + Serial.println(laenge(summe), 2); +} + +void loop() { +} + +/* + * Experimente: + * 1. Schreibe eine Funktion subtrahieren() und berechne b - a. + * 2. Deklariere eine Variable mit struct vektor c = {3, 4}; statt mit Vektor. + * Gib ihre Länge aus. + * 3. Ergänze eine Funktion skalieren(Vektor v, float faktor). + * + * Lernfragen: + * 1. Wofür steht der Name Vektor nach der typedef-Anweisung? + * 2. Warum verändert addieren() die Variable a in setup() nicht? + * 3. Wie gelangt das Ergebnis von addieren() nach setup()? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen mit typedef einen Alias für eine Struktur festlegen können." + - "Sie sollen Strukturen als Parameter und Rückgabewert von Funktionen verwenden können." +afterFocus: free +focus: + - id: alias + title: "Ein Alias mit typedef" + objective: "Die Wirkung von `typedef` erklären." + questions: + - kind: concept + text: "Wofür steht der Name `Vektor` nach der Anweisung mit `typedef`?" + - kind: recall + text: "Wie würdest du eine Variable desselben Typs ohne den Alias `Vektor` deklarieren?" + - id: struktur-als-rueckgabe + title: "Struktur als Parameter und Rückgabe" + objective: "Erklären, wie `addieren` mit Kopien arbeitet und ihr Ergebnis zurückgibt." + questions: + - kind: concept + text: "Warum verändert `addieren` die Variable `a` in `setup` nicht?" + - kind: prediction + text: "Welche Länge liefert `laenge` für einen Vektor mit x gleich 3 und y gleich 4?" + - kind: transfer + text: "Wie würdest du eine Funktion schreiben, die einen `Vektor` vom anderen subtrahiert?" +@end-unosim-tutor */ diff --git a/examples/IT08/it08-05-headerdatei.ino b/examples/IT08/it08-05-headerdatei.ino new file mode 100644 index 0000000..88fd9f7 --- /dev/null +++ b/examples/IT08/it08-05-headerdatei.ino @@ -0,0 +1,73 @@ +/* + * IT08.5 – Struktur und Funktionen in eine Headerdatei auslagern + * + * Lernziel: + * Datentyp und Funktionen in eine eigene Datei auslagern und mit #include + * in das Hauptprogramm einbinden. + * + * Bezug zu den Unterlagen: + * IT08, Folien „Funktionen in eine eigene Datei auslagern“ und + * „Beispiel 4: struct als Funktionsparameter“ (funktionen.h). + * + * Beobachtung: + * Das Programm verhält sich genau wie IT08.3. Hauptprogramm und Funktionen + * stehen jetzt aber in zwei Dateien: diesem Sketch und messung.h. + */ + +#include "messung.h" + +const byte ANZAHL_MESSSTELLEN = 3; +const byte MESSSTELLEN[ANZAHL_MESSSTELLEN] = {A0, A1, A2}; +struct Messung messungen[ANZAHL_MESSSTELLEN]; + +void setup() { + Serial.begin(115200); +} + +void loop() { + for (byte i = 0; i < ANZAHL_MESSSTELLEN; i++) { + messwertEinlesen(&messungen[i], MESSSTELLEN[i]); + } + for (byte i = 0; i < ANZAHL_MESSSTELLEN; i++) { + messwertAusgeben(&messungen[i]); + } + Serial.println(); + delay(1000); +} + +/* + * Experimente: + * 1. Öffne messung.h und ergänze in messwertAusgeben() die Einheit mV. + * 2. Kommentiere die Zeile mit #include aus und lies die Fehlermeldungen. + * 3. Ergänze in messung.h eine Funktion messwertGueltig(), die prüft, ob der + * Messwert größer als 0 V ist, und verwende sie hier. + * + * Lernfragen: + * 1. Welche Aufgabe hat die Zeile mit #include? + * 2. In welcher Datei ist messwertEinlesen() implementiert? + * 3. Welchen Vorteil bietet die Trennung bei größeren Programmen? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen Datentypen und Funktionen in eine Headerdatei auslagern und einbinden können." +afterFocus: free +focus: + - id: include + title: "Einbinden mit #include" + objective: "Die Aufgabe von `#include` erklären und die Folge ohne die Zeile vorhersagen." + questions: + - kind: concept + text: "Welche Aufgabe hat die Zeile mit `#include`?" + - kind: prediction + text: "Welche Fehlermeldungen erwartest du, wenn die Zeile mit `#include` auskommentiert wird?" + - id: organisation + title: "Programme organisieren" + objective: "Den Nutzen der Trennung in mehrere Dateien begründen." + questions: + - kind: recall + text: "In welcher Datei sind `messwertEinlesen` und `messwertAusgeben` implementiert?" + - kind: concept + text: "Welchen Vorteil bietet die Trennung in Hauptprogramm und Headerdatei bei größeren Programmen?" +@end-unosim-tutor */ diff --git a/examples/IT08/it08-06-funktion-testen.ino b/examples/IT08/it08-06-funktion-testen.ino new file mode 100644 index 0000000..9435d9b --- /dev/null +++ b/examples/IT08/it08-06-funktion-testen.ino @@ -0,0 +1,85 @@ +/* + * IT08.6 – Eine Funktion mit Testfällen prüfen + * + * Lernziel: + * Eine Funktion mit Testfällen absichern: für bekannte Eingaben das + * erwartete Ergebnis festlegen und automatisch vergleichen. + * + * Bezug zu den Unterlagen: + * IT08, Abschnitt „Funktionen testen mit Unit Tests“ (Grenzen, negative + * Werte, Genauigkeit). Auf den Folien wird dafür die Bibliothek AUnit mit + * assertEqual() verwendet; dieses Beispiel zeigt dieselbe Idee ohne Bibliothek. + * + * Beobachtung: + * Zwei Tests sind OK, ein Test meldet FEHLER: mittelwert() enthält einen + * absichtlich eingebauten Fehler. + */ + +float mittelwert(int wert1, int wert2) { + return (wert1 + wert2) / 2; // enthält einen Fehler +} + +void pruefeGleich(const char *testname, float ist, float soll) { + float abweichung = ist - soll; + if (abweichung < 0) { + abweichung = -abweichung; + } + + Serial.print(testname); + if (abweichung < 0.0001) { + Serial.println(": OK"); + } else { + Serial.print(": FEHLER, ist "); + Serial.print(ist, 2); + Serial.print(", soll "); + Serial.println(soll, 2); + } +} + +void setup() { + Serial.begin(115200); + + pruefeGleich("mittelwert(4, 8)", mittelwert(4, 8), 6.0); + pruefeGleich("mittelwert(4, 7)", mittelwert(4, 7), 5.5); + pruefeGleich("mittelwert(-3, 3)", mittelwert(-3, 3), 0.0); +} + +void loop() { +} + +/* + * Experimente: + * 1. Finde und behebe den Fehler in mittelwert(). Laufen danach alle Tests? + * 2. Ergänze einen Testfall mit zwei negativen Zahlen. + * 3. Ergänze einen Testfall nahe an der Grenze des Datentyps int. + * + * Lernfragen: + * 1. Warum besteht der erste Test, obwohl die Funktion einen Fehler enthält? + * 2. Warum vergleicht pruefeGleich() die Abweichung statt ist == soll? + * 3. Welche Testfälle sind besonders geeignet, um Fehler zu finden? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Funktion mit selbst gewählten Testfällen prüfen können." + - "Sie sollen begründen können, welche Testfälle Fehler aufdecken." +afterFocus: free +focus: + - id: testfaelle + title: "Testfälle" + objective: "Erklären, warum ein Test besteht und ein anderer scheitert." + questions: + - kind: concept + text: "Warum besteht der Test mit `mittelwert(4, 8)`, obwohl `mittelwert` einen Fehler enthält?" + - kind: concept + text: "Welche Ursache hat der FEHLER beim Test mit `mittelwert(4, 7)`?" + - id: vergleich + title: "Ergebnisse vergleichen" + objective: "Die Vergleichsmethode von `pruefeGleich` begründen und neue Testfälle wählen." + questions: + - kind: concept + text: "Warum vergleicht `pruefeGleich` die `abweichung` mit einer kleinen Schwelle statt `ist` und `soll` direkt?" + - kind: transfer + text: "Welchen weiteren Testfall würdest du ergänzen, um `mittelwert` gründlicher zu prüfen?" +@end-unosim-tutor */ diff --git a/examples/IT08/messung.h b/examples/IT08/messung.h new file mode 100644 index 0000000..2731ddc --- /dev/null +++ b/examples/IT08/messung.h @@ -0,0 +1,24 @@ +// messung.h – Datentyp und Funktionen für Messungen (IT08.5) +#pragma once + +struct Messung { + unsigned long zeit; + byte messstelle; + float messwert; +}; + +void messwertEinlesen(struct Messung *messung, byte pin) { + messung->zeit = millis(); + messung->messstelle = pin; + messung->messwert = analogRead(pin) / 1024.0 * 5.0; +} + +void messwertAusgeben(struct Messung *messung) { + Serial.print("Stelle "); + Serial.print(messung->messstelle); + Serial.print(": "); + Serial.print(messung->messwert, 2); + Serial.print(" V ("); + Serial.print(messung->zeit); + Serial.println(" ms)"); +} diff --git a/examples/IT08/rechnen.h b/examples/IT08/rechnen.h deleted file mode 100644 index 03e9995..0000000 --- a/examples/IT08/rechnen.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -int addieren(int a, int b) { - return a + b; -} diff --git a/examples/IT11/it11-01-matrix-ausgeben.ino b/examples/IT11/it11-01-matrix-ausgeben.ino index b474071..b8c48d9 100644 --- a/examples/IT11/it11-01-matrix-ausgeben.ino +++ b/examples/IT11/it11-01-matrix-ausgeben.ino @@ -1,31 +1,48 @@ /* - * IT11.1 - Eine Matrix lesbar ausgeben + * IT11.1 – Eine Matrix mit einer Funktion lesbar ausgeben * * Lernziel: - * Die Elemente einer 3x3-Matrix zeilen- und spaltenweise ausgeben. + * Eine Matrix als zweidimensionales Array speichern und mit einer Funktion + * zeilen- und spaltenweise ausgeben, mit wählbarer Genauigkeit. + * + * Bezug zu den Unterlagen: + * IT11 Matrizen, Folien „Matrizen im Speicher halten“ und „Ausgabe, schön + * lesbar“ (Schritt 1 bis 3: sequenziell, Schleifen, Genauigkeit). * * Beobachtung: - * Der serielle Monitor stellt die neun Werte in drei Zeilen dar. + * Die Matrix A erscheint in drei Zeilen mit einer Nachkommastelle; + * der Name A steht vor der mittleren Zeile. */ -const byte ANZAHL_ZEILEN = 3; -const byte ANZAHL_SPALTEN = 3; -int matrix[ANZAHL_ZEILEN][ANZAHL_SPALTEN] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} -}; +const int M = 3; // Anzahl der Zeilen +const int N = 3; // Anzahl der Spalten -void setup() { - Serial.begin(115200); +double A[M][N] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } +}; - for (byte zeile = 0; zeile < ANZAHL_ZEILEN; zeile++) { - for (byte spalte = 0; spalte < ANZAHL_SPALTEN; spalte++) { - Serial.print(matrix[zeile][spalte]); +void printMatrix(const double mat[M][N], const char *name = "M", byte precision = 2) { + for (int i = 0; i < M; i++) { + if (i == 1) { + Serial.print(name); + Serial.print(" = \t"); + } else { + Serial.print("\t"); + } + for (int k = 0; k < N; k++) { + Serial.print(mat[i][k], precision); Serial.print("\t"); } Serial.println(); } + Serial.println(); +} + +void setup() { + Serial.begin(115200); + printMatrix(A, "A", 1); } void loop() { @@ -33,35 +50,38 @@ void loop() { /* * Experimente: - * 1. Aendere die Werte der Hauptdiagonalen zu 0. - * 2. Vertausche bei der Ausgabe zeile und spalte. + * 1. Rufe printMatrix(A); ohne die letzten beiden Argumente auf. + * 2. Gib A mit 0 und mit 3 Nachkommastellen aus. + * 3. Setze die Hauptdiagonale von A auf 0 und gib A erneut aus. * * Lernfragen: - * 1. Welches Element wird mit matrix[1][2] angesprochen? - * 2. Warum werden zwei Schleifen benoetigt? - * 3. Welche Aufgabe hat das Tabulatorzeichen \t? + * 1. Welches Element wird mit A[1][2] angesprochen? + * 2. Warum werden zwei Schleifen benötigt? + * 3. Was geschieht, wenn beim Aufruf name und precision weggelassen werden? */ /* @unosim-tutor schemaVersion: 2 learningObjectives: - - "Die Studierenden sollen eine Matrix zeilen- und spaltenweise ausgeben können." + - "Die Studierenden sollen eine Matrix als zweidimensionales Array speichern und mit einer Funktion ausgeben können." afterFocus: free focus: - - id: elementzugriff - title: "Elementzugriff" + - id: elemente + title: "Elemente der Matrix" objective: "Ein Element über zwei Indizes bestimmen und die Zahl der Schleifen begründen." questions: - - kind: prediction - text: "Welches Element von `matrix` wird mit den Indizes 1 und 2 angesprochen?" + - kind: application + text: "Welcher Wert steht in `A` an der Position mit dem Zeilenindex 1 und dem Spaltenindex 2?" - kind: concept - text: "Warum werden zwei Schleifen benötigt?" - - id: ausgabeformat - title: "Ausgabeformat" - objective: "Die Aufgabe des Tabulators erklären und die Ausgabe verändern." + text: "Warum werden in `printMatrix` zwei Schleifen benötigt?" + - id: parameter + title: "Parameter der Ausgabefunktion" + objective: "Die Parameter von `printMatrix` und ihre Standardwerte erklären." questions: - kind: concept - text: "Welche Aufgabe hat das Tabulatorzeichen `\\t`?" - - kind: transfer - text: "Wie würdest du die Matrix transponiert ausgeben?" + text: "Welche Aufgabe hat der Parameter `precision`?" + - kind: prediction + text: "Wie sieht die Ausgabe aus, wenn `printMatrix` nur mit `A` aufgerufen wird?" + - kind: concept + text: "Warum ist der Parameter `mat` als `const` deklariert?" @end-unosim-tutor */ diff --git a/examples/IT11/it11-02-matrix-transponieren.ino b/examples/IT11/it11-02-matrix-transponieren.ino deleted file mode 100644 index 10ff4b7..0000000 --- a/examples/IT11/it11-02-matrix-transponieren.ino +++ /dev/null @@ -1,71 +0,0 @@ -/* - * IT11.2 - Eine Matrix transponieren - * - * Lernziel: - * Beim Transponieren Zeilen- und Spaltenindizes vertauschen. - * - * Beobachtung: - * Aus dem Element [0][1] der Ausgangsmatrix wird [1][0] im Ergebnis. - */ - -const byte GROESSE = 3; -int matrix[GROESSE][GROESSE] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} -}; -int transponiert[GROESSE][GROESSE]; - -void setup() { - Serial.begin(115200); - - for (byte zeile = 0; zeile < GROESSE; zeile++) { - for (byte spalte = 0; spalte < GROESSE; spalte++) { - transponiert[spalte][zeile] = matrix[zeile][spalte]; - } - } - - for (byte zeile = 0; zeile < GROESSE; zeile++) { - for (byte spalte = 0; spalte < GROESSE; spalte++) { - Serial.print(transponiert[zeile][spalte]); - Serial.print("\t"); - } - Serial.println(); - } -} - -void loop() { -} - -/* - * Experimente: - * 1. Verwende eine Diagonalmatrix. - * 2. Gib Ausgangsmatrix und Ergebnis untereinander aus. - * - * Lernfragen: - * 1. Welche beiden Indizes werden beim Transponieren vertauscht? - * 2. Was geschieht mit den Elementen auf der Hauptdiagonalen? - * 3. Welche Eigenschaft hat das Ergebnis bei einer symmetrischen Matrix? - */ - -/* @unosim-tutor -schemaVersion: 2 -learningObjectives: - - "Die Studierenden sollen eine Matrix transponieren können." -afterFocus: free -focus: - - id: transponieren - title: "Transponieren" - objective: "Die vertauschten Indizes und die Hauptdiagonale erklären." - questions: - - kind: concept - text: "Welche beiden Indizes werden beim Transponieren vertauscht?" - - kind: concept - text: "Was geschieht mit den Elementen auf der Hauptdiagonalen?" - - id: ergebnis - title: "Das Ergebnis" - objective: "Ein Element der transponierten Matrix vorhersagen." - questions: - - kind: prediction - text: "Welcher Wert steht nach dem Transponieren in `transponiert` an der Position mit den Indizes 1 und 0?" -@end-unosim-tutor */ diff --git a/examples/IT11/it11-02-skalarmultiplikation.ino b/examples/IT11/it11-02-skalarmultiplikation.ino new file mode 100644 index 0000000..14406fa --- /dev/null +++ b/examples/IT11/it11-02-skalarmultiplikation.ino @@ -0,0 +1,79 @@ +/* + * IT11.2 – Funktionen auslagern und eine Matrix skalieren + * + * Lernziel: + * Die Ausgabefunktion in mat_func.h auslagern und eine Funktion schreiben, + * die jedes Element einer Matrix mit einem Faktor multipliziert. + * + * Bezug zu den Unterlagen: + * IT11 Matrizen, Schritt 4 „Organisieren: Auslagern“ und Schritt 5 + * „Funktionen erweitern: Skalar-Multiplikation“. + * + * Beobachtung: + * Zuerst erscheint A, danach 2*A: Jedes Element ist doppelt so groß. + * M, N und printMatrix() stehen in mat_func.h. + */ + +#include "mat_func.h" + +double A[M][N] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } +}; + +void scaleMatrix(double mat[M][N], double faktor) { + for (byte i = 0; i < M; i++) { + for (byte k = 0; k < N; k++) { + mat[i][k] *= faktor; + } + } +} + +void setup() { + Serial.begin(115200); + printMatrix(A, "A", 0); + scaleMatrix(A, 2); + printMatrix(A, "2*A", 0); +} + +void loop() { +} + +/* + * Experimente: + * 1. Skaliere A mit dem Faktor 0.5 und gib das Ergebnis mit einer + * Nachkommastelle aus. + * 2. Rufe scaleMatrix(A, 2); zweimal auf. + * 3. Öffne mat_func.h und ändere dort die Ausgabe des Namens. + * + * Lernfragen: + * 1. Warum verändert scaleMatrix() die Matrix A selbst? + * 2. Warum ist der Parameter mat hier nicht const? + * 3. Welchen Vorteil hat es, printMatrix() in mat_func.h auszulagern? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Matrix in einer Funktion elementweise verändern können." + - "Sie sollen Funktionen in eine Headerdatei auslagern und wiederverwenden können." +afterFocus: free +focus: + - id: skalieren + title: "Elementweise skalieren" + objective: "Erklären, wie `scaleMatrix` jedes Element verändert." + questions: + - kind: concept + text: "Warum verändert `scaleMatrix` die Matrix `A` selbst und nicht eine Kopie?" + - kind: prediction + text: "Welcher Wert steht in der Mitte von `A`, nachdem `scaleMatrix(A, 2)` zweimal aufgerufen wurde?" + - id: auslagern + title: "Auslagern und const" + objective: "Den Unterschied zwischen lesender und verändernder Funktion begründen." + questions: + - kind: concept + text: "Warum ist `mat` in `scaleMatrix` anders als in `printMatrix` nicht als konstant deklariert?" + - kind: concept + text: "Welchen Vorteil hat es, dass `printMatrix` in einer eigenen Datei steht?" +@end-unosim-tutor */ diff --git a/examples/IT11/it11-03-matrizen-addieren.ino b/examples/IT11/it11-03-matrizen-addieren.ino index 882a0ca..afe6efc 100644 --- a/examples/IT11/it11-03-matrizen-addieren.ino +++ b/examples/IT11/it11-03-matrizen-addieren.ino @@ -1,52 +1,60 @@ /* - * IT11.3 - Zwei Matrizen addieren + * IT11.3 – Zwei Matrizen addieren * * Lernziel: - * Zwei Matrizen elementweise addieren. + * Zwei Matrizen gleicher Größe elementweise addieren. + * + * Bezug zu den Unterlagen: + * IT11 Matrizen, Schritt 6 „Funktionen erweitern: Matrix-Addition“. * * Beobachtung: - * Jedes Element der Ergebnismatrix ist die Summe derselben Position in A und B. + * Wie auf der Folie: A, A+B und B. In A+B steht überall 10, weil sich die + * Elemente von A und B an jeder Position zu 10 ergänzen. */ -const byte GROESSE = 3; -int matrixA[GROESSE][GROESSE] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} -}; -int matrixB[GROESSE][GROESSE] = { - {9, 8, 7}, - {6, 5, 4}, - {3, 2, 1} +#include "mat_func.h" + +double A[M][N] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } }; -int summe[GROESSE][GROESSE]; -void setup() { - Serial.begin(115200); +double B[M][N] = { + { 9, 8, 7 }, + { 6, 5, 4 }, + { 3, 2, 1 } +}; - for (byte zeile = 0; zeile < GROESSE; zeile++) { - for (byte spalte = 0; spalte < GROESSE; spalte++) { - summe[zeile][spalte] = - matrixA[zeile][spalte] + matrixB[zeile][spalte]; - Serial.print(summe[zeile][spalte]); - Serial.print("\t"); +void addMatrix(double mat1[M][N], const double mat2[M][N]) { + for (byte i = 0; i < M; i++) { + for (byte k = 0; k < N; k++) { + mat1[i][k] += mat2[i][k]; } - Serial.println(); } } +void setup() { + Serial.begin(115200); + printMatrix(A, "A", 0); + addMatrix(A, B); + printMatrix(A, "A+B", 0); + printMatrix(B, "B", 0); +} + void loop() { } /* * Experimente: - * 1. Setze alle Elemente von matrixB auf 0. - * 2. Addiere eine Matrix mit negativen Elementen. + * 1. Ändere ein Element von B. An welcher Stelle von A+B ändert sich das Ergebnis? + * 2. Rufe addMatrix(A, B); zweimal auf. + * 3. Schreibe eine Funktion subMatrix(), die B von A subtrahiert. * * Lernfragen: * 1. Welche Elemente werden miteinander addiert? - * 2. Warum muessen beide Matrizen dieselben Abmessungen besitzen? - * 3. Welchen Wert hat summe[2][1] im Ausgangsbeispiel? + * 2. Warum müssen beide Matrizen dieselben Abmessungen besitzen? + * 3. Warum steht nach dem Aufruf das Ergebnis in A und nicht in einer neuen Matrix? */ /* @unosim-tutor @@ -55,18 +63,20 @@ learningObjectives: - "Die Studierenden sollen zwei Matrizen elementweise addieren können." afterFocus: free focus: - - id: addition + - id: elementweise title: "Elementweise Addition" objective: "Erklären, welche Elemente addiert werden und warum gleiche Abmessungen nötig sind." questions: - kind: concept - text: "Welche Elemente werden in `summe[zeile][spalte]` miteinander addiert?" + text: "Welche Elemente werden in `mat1[i][k] += mat2[i][k]` miteinander addiert?" - kind: concept - text: "Warum müssen `matrixA` und `matrixB` dieselben Abmessungen besitzen?" + text: "Warum müssen `A` und `B` dieselben Abmessungen besitzen?" - id: ergebnis - title: "Das Ergebnis" - objective: "Ein Element der Summe vorhersagen." + title: "Wo das Ergebnis steht" + objective: "Die Wirkung von `addMatrix` auf `A` beschreiben und vorhersagen." questions: - kind: prediction - text: "Welchen Wert hat das Element von `summe` mit den Indizes 2 und 1 im Ausgangsbeispiel?" + text: "Welche Werte stehen in `A`, wenn `addMatrix(A, B)` zweimal aufgerufen wird?" + - kind: transfer + text: "Wie würdest du die Funktion ändern, damit `A` unverändert bleibt und die Summe in einer dritten Matrix steht?" @end-unosim-tutor */ diff --git a/examples/IT11/it11-04-matrix-transponieren.ino b/examples/IT11/it11-04-matrix-transponieren.ino new file mode 100644 index 0000000..e2b7495 --- /dev/null +++ b/examples/IT11/it11-04-matrix-transponieren.ino @@ -0,0 +1,79 @@ +/* + * IT11.4 – Eine Matrix transponieren + * + * Lernziel: + * Beim Transponieren Zeilen- und Spaltenindex vertauschen und das Ergebnis + * in eine zweite Matrix schreiben. + * + * Bezug zu den Unterlagen: + * IT11 Matrizen, Schritt 7 „Funktionen erweitern: Transponieren“. + * + * Beobachtung: + * Die erste Zeile von A wird zur ersten Spalte von AT. Die Hauptdiagonale + * 1, 5, 9 bleibt unverändert. + */ + +#include "mat_func.h" + +double A[M][N] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } +}; + +double AT[N][M]; + +void transposeMatrix(const double mat[M][N], double ergebnis[N][M]) { + for (byte i = 0; i < M; i++) { + for (byte k = 0; k < N; k++) { + ergebnis[k][i] = mat[i][k]; + } + } +} + +void setup() { + Serial.begin(115200); + printMatrix(A, "A", 0); + transposeMatrix(A, AT); + printMatrix(AT, "AT", 0); +} + +void loop() { +} + +/* + * Experimente: + * 1. Verwende eine symmetrische Matrix, z. B. mit A[0][1] = A[1][0] = 2. + * 2. Transponiere AT ein zweites Mal. Welche Matrix entsteht? + * 3. Versuche, A ohne zweite Matrix direkt in sich selbst zu transponieren. + * Warum gehen dabei Werte verloren? + * + * Lernfragen: + * 1. Welche beiden Indizes werden beim Transponieren vertauscht? + * 2. Was geschieht mit den Elementen auf der Hauptdiagonalen? + * 3. Warum wird das Ergebnis in eine zweite Matrix geschrieben? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen eine Matrix transponieren und die Vertauschung der Indizes erklären können." +afterFocus: free +focus: + - id: indizes-tauschen + title: "Indizes vertauschen" + objective: "Die vertauschten Indizes und die Hauptdiagonale erklären." + questions: + - kind: concept + text: "Welche beiden Indizes werden in `ergebnis[k][i] = mat[i][k]` vertauscht?" + - kind: prediction + text: "Welcher Wert steht in `AT` an der Position mit dem Zeilenindex 0 und dem Spaltenindex 2?" + - id: zweite-matrix + title: "Eine zweite Matrix" + objective: "Begründen, warum das Ergebnis in eine eigene Matrix geschrieben wird." + questions: + - kind: concept + text: "Warum schreibt `transposeMatrix` das Ergebnis in `ergebnis` und nicht zurück in `mat`?" + - kind: transfer + text: "Wie würdest du mit `transposeMatrix` prüfen, ob eine Matrix symmetrisch ist?" +@end-unosim-tutor */ diff --git a/examples/IT11/it11-05-matrizen-multiplizieren.ino b/examples/IT11/it11-05-matrizen-multiplizieren.ino new file mode 100644 index 0000000..008cc80 --- /dev/null +++ b/examples/IT11/it11-05-matrizen-multiplizieren.ino @@ -0,0 +1,121 @@ +/* + * IT11.5 – Matrizen multiplizieren und die Einheitsmatrix prüfen + * + * Lernziel: + * Zwei Matrizen nach der Regel „Zeile mal Spalte“ multiplizieren und + * prüfen, ob eine Matrix die Einheitsmatrix ist. + * + * Bezug zu den Unterlagen: + * IT11 Matrizen, Schritt 8 „Identität prüfen“ und Schritt 9 + * „Matrizenmultiplikation“. + * + * Beobachtung: + * E ist die Einheitsmatrix (Ausgabe 1). A*E ergibt wieder A. + * A*B ergibt neue Werte, z. B. oben links 1*9 + 2*6 + 3*3 = 30. + */ + +#include "mat_func.h" + +double A[M][N] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } +}; + +double B[M][N] = { + { 9, 8, 7 }, + { 6, 5, 4 }, + { 3, 2, 1 } +}; + +double E[M][N] = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } +}; + +double C[M][N]; + +bool isIdentity(const double mat[M][N]) { + for (byte i = 0; i < M; i++) { + for (byte k = 0; k < N; k++) { + double soll = 0; // außerhalb der Hauptdiagonalen 0 ... + if (i == k) { + soll = 1; // ... auf der Hauptdiagonalen 1 + } + if (mat[i][k] != soll) { + return false; + } + } + } + return true; +} + +void multiplyMatrix(const double mat1[M][N], const double mat2[M][N], double ergebnis[M][N]) { + for (byte i = 0; i < M; i++) { + for (byte k = 0; k < N; k++) { + ergebnis[i][k] = 0; + for (byte j = 0; j < N; j++) { + ergebnis[i][k] += mat1[i][j] * mat2[j][k]; + } + } + } +} + +void setup() { + Serial.begin(115200); + + Serial.print("E ist Einheitsmatrix: "); + Serial.println(isIdentity(E)); + Serial.println(); + + multiplyMatrix(A, E, C); + printMatrix(C, "A*E", 0); + + multiplyMatrix(A, B, C); + printMatrix(C, "A*B", 0); +} + +void loop() { +} + +/* + * Experimente: + * 1. Prüfe mit isIdentity(), ob A die Einheitsmatrix ist. + * 2. Berechne B*A und vergleiche mit A*B. + * 3. Rechne das Element in der Mitte von A*B von Hand nach. + * + * Lernfragen: + * 1. Welche Zeile und welche Spalte werden für ergebnis[i][k] verwendet? + * 2. Warum werden für die Multiplikation drei Schleifen benötigt? + * 3. Warum wird ergebnis[i][k] vor der innersten Schleife auf 0 gesetzt? + */ + +/* @unosim-tutor +schemaVersion: 2 +learningObjectives: + - "Die Studierenden sollen zwei Matrizen nach der Regel Zeile mal Spalte multiplizieren können." + - "Sie sollen prüfen können, ob eine Matrix die Einheitsmatrix ist." +afterFocus: free +focus: + - id: zeile-mal-spalte + title: "Zeile mal Spalte" + objective: "Erklären, wie ein Element des Produkts berechnet wird." + questions: + - kind: concept + text: "Welche Zeile von `mat1` und welche Spalte von `mat2` werden für `ergebnis[i][k]` verwendet?" + - kind: concept + text: "Warum wird `ergebnis[i][k]` vor der innersten Schleife auf 0 gesetzt?" + - kind: application + text: "Welcher Wert steht in der Mitte von `A*B`?" + - id: einheitsmatrix + title: "Die Einheitsmatrix" + objective: "Die Prüfung in `isIdentity` erklären und das Produkt mit `E` vorhersagen." + questions: + - kind: concept + text: "Woran erkennt `isIdentity`, dass eine Matrix keine Einheitsmatrix ist?" + - kind: prediction + text: "Welches Ergebnis liefert `multiplyMatrix(A, E, C)`, und warum?" + - kind: transfer + text: "Wie würdest du mit `multiplyMatrix` prüfen, ob die Reihenfolge der Faktoren das Ergebnis verändert?" +@end-unosim-tutor */ diff --git a/examples/IT11/mat_func.h b/examples/IT11/mat_func.h new file mode 100644 index 0000000..c7aa9f3 --- /dev/null +++ b/examples/IT11/mat_func.h @@ -0,0 +1,22 @@ +// mat_func.h – Matrixgröße und Ausgabefunktion (IT11.2 bis IT11.5) +#pragma once + +const int M = 3; // Anzahl der Zeilen +const int N = 3; // Anzahl der Spalten + +void printMatrix(const double mat[M][N], const char *name = "M", byte precision = 2) { + for (int i = 0; i < M; i++) { + if (i == 1) { + Serial.print(name); + Serial.print(" = \t"); + } else { + Serial.print("\t"); + } + for (int k = 0; k < N; k++) { + Serial.print(mat[i][k], precision); + Serial.print("\t"); + } + Serial.println(); + } + Serial.println(); +} diff --git a/manifest.json b/manifest.json index b5dc077..3dc90f3 100644 --- a/manifest.json +++ b/manifest.json @@ -1,14 +1,14 @@ { "schemaVersion": 2, "repository": "MoDevIO/UnoSim-Examples", - "ref": "v2.0.0", + "ref": "v3.0.0", "examples": [ { - "id": "it01-01-variable-speichern", - "title": "IT01.1 – Einen Wert in einer Variablen speichern", + "id": "it01-01-variable-definieren", + "title": "IT01.1 – Eine Variable deklarieren, initialisieren und zuweisen", "category": "IT01 – Daten", - "files": [{ "name": "it01-01-variable-speichern.ino", "path": "examples/IT01/it01-01-variable-speichern.ino" }], - "main": "it01-01-variable-speichern.ino" + "files": [{ "name": "it01-01-variable-definieren.ino", "path": "examples/IT01/it01-01-variable-definieren.ino" }], + "main": "it01-01-variable-definieren.ino" }, { "id": "it01-02-konstante", @@ -17,239 +17,363 @@ "files": [{ "name": "it01-02-konstante.ino", "path": "examples/IT01/it01-02-konstante.ino" }], "main": "it01-02-konstante.ino" }, + { + "id": "it01-03-datentyp-waehlen", + "title": "IT01.3 – Einen Datentyp begründet auswählen", + "category": "IT01 – Daten", + "files": [{ "name": "it01-03-datentyp-waehlen.ino", "path": "examples/IT01/it01-03-datentyp-waehlen.ino" }], + "main": "it01-03-datentyp-waehlen.ino" + }, + { + "id": "it01-04-zeichen-ascii", + "title": "IT01.4 – Zeichen und ihre Codes", + "category": "IT01 – Daten", + "files": [{ "name": "it01-04-zeichen-ascii.ino", "path": "examples/IT01/it01-04-zeichen-ascii.ino" }], + "main": "it01-04-zeichen-ascii.ino" + }, + { + "id": "it01-05-global-lokal", + "title": "IT01.5 – Globale und lokale Variablen", + "category": "IT01 – Daten", + "files": [{ "name": "it01-05-global-lokal.ino", "path": "examples/IT01/it01-05-global-lokal.ino" }], + "main": "it01-05-global-lokal.ino" + }, { "id": "it02-01-arithmetische-operatoren", - "title": "IT02.1 – Arithmetische Operatoren", + "title": "IT02.1 – Mit arithmetischen Operatoren rechnen", "category": "IT02 – Datenmanipulation", "files": [{ "name": "it02-01-arithmetische-operatoren.ino", "path": "examples/IT02/it02-01-arithmetische-operatoren.ino" }], "main": "it02-01-arithmetische-operatoren.ino" }, { "id": "it02-02-vergleichsoperatoren", - "title": "IT02.2 – Vergleichsoperatoren", + "title": "IT02.2 – Zwei Werte vergleichen", "category": "IT02 – Datenmanipulation", "files": [{ "name": "it02-02-vergleichsoperatoren.ino", "path": "examples/IT02/it02-02-vergleichsoperatoren.ino" }], "main": "it02-02-vergleichsoperatoren.ino" }, { "id": "it02-03-boolesche-operatoren", - "title": "IT02.3 – Boolesche Operatoren", + "title": "IT02.3 – Wahrheitswerte verknüpfen", "category": "IT02 – Datenmanipulation", "files": [{ "name": "it02-03-boolesche-operatoren.ino", "path": "examples/IT02/it02-03-boolesche-operatoren.ino" }], "main": "it02-03-boolesche-operatoren.ino" }, { - "id": "it02-04-bitoperatoren", - "title": "IT02.4 – Bitoperatoren", + "id": "it02-04-wertebereich-ueberlauf", + "title": "IT02.4 – Die Grenze eines Datentyps sichtbar machen", + "category": "IT02 – Datenmanipulation", + "files": [{ "name": "it02-04-wertebereich-ueberlauf.ino", "path": "examples/IT02/it02-04-wertebereich-ueberlauf.ino" }], + "main": "it02-04-wertebereich-ueberlauf.ino" + }, + { + "id": "it02-05-bitoperatoren", + "title": "IT02.5 – Einzelne Bits prüfen und setzen (optional)", "category": "IT02 – Datenmanipulation", - "files": [{ "name": "it02-04-bitoperatoren.ino", "path": "examples/IT02/it02-04-bitoperatoren.ino" }], - "main": "it02-04-bitoperatoren.ino" + "files": [{ "name": "it02-05-bitoperatoren.ino", "path": "examples/IT02/it02-05-bitoperatoren.ino" }], + "main": "it02-05-bitoperatoren.ino" }, { "id": "it03-01-serielle-ausgabe", - "title": "IT03.1 – Serielle Ausgabe", - "category": "IT03 – Ein- und Ausgabe", + "title": "IT03.1 – Text und Zahlen seriell ausgeben", + "category": "IT03 – Ein- und Ausgabe 1", "files": [{ "name": "it03-01-serielle-ausgabe.ino", "path": "examples/IT03/it03-01-serielle-ausgabe.ino" }], "main": "it03-01-serielle-ausgabe.ino" }, { - "id": "it03-02-serielles-zeichen-einlesen", - "title": "IT03.2 – Serielles Zeichen einlesen", - "category": "IT03 – Ein- und Ausgabe", - "files": [{ "name": "it03-02-serielles-zeichen-einlesen.ino", "path": "examples/IT03/it03-02-serielles-zeichen-einlesen.ino" }], - "main": "it03-02-serielles-zeichen-einlesen.ino" + "id": "it03-02-zeichen-einlesen", + "title": "IT03.2 – Zeichen seriell einlesen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-02-zeichen-einlesen.ino", "path": "examples/IT03/it03-02-zeichen-einlesen.ino" }], + "main": "it03-02-zeichen-einlesen.ino" + }, + { + "id": "it03-03-zahl-einlesen", + "title": "IT03.3 – Eine Zahl am Stück einlesen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-03-zahl-einlesen.ino", "path": "examples/IT03/it03-03-zahl-einlesen.ino" }], + "main": "it03-03-zahl-einlesen.ino" + }, + { + "id": "it03-04-led-blinken", + "title": "IT03.4 – Eine LED blinken lassen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-04-led-blinken.ino", "path": "examples/IT03/it03-04-led-blinken.ino" }], + "main": "it03-04-led-blinken.ino" }, { - "id": "it03-03-digitaler-ausgang", - "title": "IT03.3 – Digitaler Ausgang", - "category": "IT03 – Ein- und Ausgabe", - "files": [{ "name": "it03-03-digitaler-ausgang.ino", "path": "examples/IT03/it03-03-digitaler-ausgang.ino" }], - "main": "it03-03-digitaler-ausgang.ino" + "id": "it03-05-pins-als-konstanten", + "title": "IT03.5 – Pin-Nummern als Konstanten", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-05-pins-als-konstanten.ino", "path": "examples/IT03/it03-05-pins-als-konstanten.ino" }], + "main": "it03-05-pins-als-konstanten.ino" }, { - "id": "it03-04-digitaler-eingang", - "title": "IT03.4 – Digitaler Eingang", - "category": "IT03 – Ein- und Ausgabe", - "files": [{ "name": "it03-04-digitaler-eingang.ino", "path": "examples/IT03/it03-04-digitaler-eingang.ino" }], - "main": "it03-04-digitaler-eingang.ino" + "id": "it03-06-digitaler-eingang", + "title": "IT03.6 – Einen digitalen Eingang lesen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-06-digitaler-eingang.ino", "path": "examples/IT03/it03-06-digitaler-eingang.ino" }], + "main": "it03-06-digitaler-eingang.ino" }, { - "id": "it03-05-millis", - "title": "IT03.5 – Vergangene Zeit mit millis()", - "category": "IT03 – Ein- und Ausgabe", - "files": [{ "name": "it03-05-millis.ino", "path": "examples/IT03/it03-05-millis.ino" }], - "main": "it03-05-millis.ino" + "id": "it03-07-zeit-messen", + "title": "IT03.7 – Eine Zeitdifferenz messen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-07-zeit-messen.ino", "path": "examples/IT03/it03-07-zeit-messen.ino" }], + "main": "it03-07-zeit-messen.ino" + }, + { + "id": "it03-08-map", + "title": "IT03.8 – Werte mit map() umrechnen", + "category": "IT03 – Ein- und Ausgabe 1", + "files": [{ "name": "it03-08-map.ino", "path": "examples/IT03/it03-08-map.ino" }], + "main": "it03-08-map.ino" }, { "id": "it04-01-analoger-eingang", - "title": "IT04.1 – Analoger Eingang", - "category": "IT04 – Analoge Ein- und Ausgabe", + "title": "IT04.1 – Einen analogen Eingang lesen", + "category": "IT04 – Ein- und Ausgabe 2", "files": [{ "name": "it04-01-analoger-eingang.ino", "path": "examples/IT04/it04-01-analoger-eingang.ino" }], "main": "it04-01-analoger-eingang.ino" }, { - "id": "it04-02-messwert-in-volt", - "title": "IT04.2 – Messwert in Volt umrechnen", - "category": "IT04 – Analoge Ein- und Ausgabe", - "files": [{ "name": "it04-02-messwert-in-volt.ino", "path": "examples/IT04/it04-02-messwert-in-volt.ino" }], - "main": "it04-02-messwert-in-volt.ino" + "id": "it04-02-spannung-berechnen", + "title": "IT04.2 – Einen Messwert in Volt umrechnen", + "category": "IT04 – Ein- und Ausgabe 2", + "files": [{ "name": "it04-02-spannung-berechnen.ino", "path": "examples/IT04/it04-02-spannung-berechnen.ino" }], + "main": "it04-02-spannung-berechnen.ino" }, { "id": "it04-03-pwm-ausgang", - "title": "IT04.3 – PWM-Ausgang", - "category": "IT04 – Analoge Ein- und Ausgabe", + "title": "IT04.3 – Einen Ausgang mit PWM ansteuern", + "category": "IT04 – Ein- und Ausgabe 2", "files": [{ "name": "it04-03-pwm-ausgang.ino", "path": "examples/IT04/it04-03-pwm-ausgang.ino" }], "main": "it04-03-pwm-ausgang.ino" }, { - "id": "it04-04-map", - "title": "IT04.4 – Werte mit map() umrechnen", - "category": "IT04 – Analoge Ein- und Ausgabe", - "files": [{ "name": "it04-04-map.ino", "path": "examples/IT04/it04-04-map.ino" }], - "main": "it04-04-map.ino" + "id": "it04-04-taster-flanke", + "title": "IT04.4 – Einen Tasterwechsel erkennen", + "category": "IT04 – Ein- und Ausgabe 2", + "files": [{ "name": "it04-04-taster-flanke.ino", "path": "examples/IT04/it04-04-taster-flanke.ino" }], + "main": "it04-04-taster-flanke.ino" + }, + { + "id": "it04-05-helligkeit-steuern", + "title": "IT04.5 – Mit einem Sensorwert die Helligkeit steuern", + "category": "IT04 – Ein- und Ausgabe 2", + "files": [{ "name": "it04-05-helligkeit-steuern.ino", "path": "examples/IT04/it04-05-helligkeit-steuern.ino" }], + "main": "it04-05-helligkeit-steuern.ino" }, { "id": "it05-01-if", - "title": "IT05.1 – Einfache if-Verzweigung", + "title": "IT05.1 – Eine Anweisung bedingt ausführen", "category": "IT05 – Verzweigungen", "files": [{ "name": "it05-01-if.ino", "path": "examples/IT05/it05-01-if.ino" }], "main": "it05-01-if.ino" }, { "id": "it05-02-if-else", - "title": "IT05.2 – Verzweigung mit if/else", + "title": "IT05.2 – Zwischen zwei Alternativen entscheiden", "category": "IT05 – Verzweigungen", "files": [{ "name": "it05-02-if-else.ino", "path": "examples/IT05/it05-02-if-else.ino" }], "main": "it05-02-if-else.ino" }, { - "id": "it05-03-switch", - "title": "IT05.3 – Mehrfachverzweigung mit switch", + "id": "it05-03-else-if", + "title": "IT05.3 – Mehrere Bedingungen nacheinander prüfen", + "category": "IT05 – Verzweigungen", + "files": [{ "name": "it05-03-else-if.ino", "path": "examples/IT05/it05-03-else-if.ino" }], + "main": "it05-03-else-if.ino" + }, + { + "id": "it05-04-switch-menue", + "title": "IT05.4 – Ein Menü mit switch", + "category": "IT05 – Verzweigungen", + "files": [{ "name": "it05-04-switch-menue.ino", "path": "examples/IT05/it05-04-switch-menue.ino" }], + "main": "it05-04-switch-menue.ino" + }, + { + "id": "it05-05-intervall-ohne-delay", + "title": "IT05.5 – Alle 500 ms etwas tun, ohne delay()", "category": "IT05 – Verzweigungen", - "files": [{ "name": "it05-03-switch.ino", "path": "examples/IT05/it05-03-switch.ino" }], - "main": "it05-03-switch.ino" + "files": [{ "name": "it05-05-intervall-ohne-delay.ino", "path": "examples/IT05/it05-05-intervall-ohne-delay.ino" }], + "main": "it05-05-intervall-ohne-delay.ino" }, { "id": "it06-01-while", - "title": "IT06.1 – Kopfgesteuerte while-Schleife", + "title": "IT06.1 – Eine kopfgesteuerte Schleife", "category": "IT06 – Schleifen", "files": [{ "name": "it06-01-while.ino", "path": "examples/IT06/it06-01-while.ino" }], "main": "it06-01-while.ino" }, { "id": "it06-02-do-while", - "title": "IT06.2 – Fußgesteuerte do/while-Schleife", + "title": "IT06.2 – Eine fußgesteuerte Schleife", "category": "IT06 – Schleifen", "files": [{ "name": "it06-02-do-while.ino", "path": "examples/IT06/it06-02-do-while.ino" }], "main": "it06-02-do-while.ino" }, { "id": "it06-03-for", - "title": "IT06.3 – Zählschleife mit for", + "title": "IT06.3 – Zählschleifen mit for", "category": "IT06 – Schleifen", "files": [{ "name": "it06-03-for.ino", "path": "examples/IT06/it06-03-for.ino" }], "main": "it06-03-for.ino" }, { - "id": "it06-04-verschachtelte-schleifen", - "title": "IT06.4 – Verschachtelte Schleifen", + "id": "it06-04-binaerzahl", + "title": "IT06.4 – Eine Zahl binär ausgeben", "category": "IT06 – Schleifen", - "files": [{ "name": "it06-04-verschachtelte-schleifen.ino", "path": "examples/IT06/it06-04-verschachtelte-schleifen.ino" }], - "main": "it06-04-verschachtelte-schleifen.ino" + "files": [{ "name": "it06-04-binaerzahl.ino", "path": "examples/IT06/it06-04-binaerzahl.ino" }], + "main": "it06-04-binaerzahl.ino" + }, + { + "id": "it06-05-verschachtelte-schleifen", + "title": "IT06.5 – Zwei Schleifen verschachteln", + "category": "IT06 – Schleifen", + "files": [{ "name": "it06-05-verschachtelte-schleifen.ino", "path": "examples/IT06/it06-05-verschachtelte-schleifen.ino" }], + "main": "it06-05-verschachtelte-schleifen.ino" }, { "id": "it07-01-funktion-ohne-rueckgabe", - "title": "IT07.1 – Funktion ohne Rückgabe", + "title": "IT07.1 – Wiederkehrende Abläufe in Funktionen bündeln", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-01-funktion-ohne-rueckgabe.ino", "path": "examples/IT07/it07-01-funktion-ohne-rueckgabe.ino" }], "main": "it07-01-funktion-ohne-rueckgabe.ino" }, { "id": "it07-02-funktion-mit-rueckgabe", - "title": "IT07.2 – Funktion mit Rückgabe", + "title": "IT07.2 – Einen Wert aus einer Funktion zurückgeben", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-02-funktion-mit-rueckgabe.ino", "path": "examples/IT07/it07-02-funktion-mit-rueckgabe.ino" }], "main": "it07-02-funktion-mit-rueckgabe.ino" }, { "id": "it07-03-call-by-value", - "title": "IT07.3 – Call-by-value", + "title": "IT07.3 – Einen Parameter als Wert übergeben (Call-by-value)", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-03-call-by-value.ino", "path": "examples/IT07/it07-03-call-by-value.ino" }], "main": "it07-03-call-by-value.ino" }, { "id": "it07-04-call-by-reference", - "title": "IT07.4 – Call-by-reference", + "title": "IT07.4 – Die Adresse einer Variablen übergeben (Call-by-reference)", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-04-call-by-reference.ino", "path": "examples/IT07/it07-04-call-by-reference.ino" }], "main": "it07-04-call-by-reference.ino" }, { "id": "it07-05-eindimensionales-array", - "title": "IT07.5 – Eindimensionales Array", + "title": "IT07.5 – Gleichartige Werte in einem Array speichern", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-05-eindimensionales-array.ino", "path": "examples/IT07/it07-05-eindimensionales-array.ino" }], "main": "it07-05-eindimensionales-array.ino" }, { "id": "it07-06-zweidimensionales-array", - "title": "IT07.6 – Zweidimensionales Array", + "title": "IT07.6 – Ein Tastenfeld als zweidimensionales Array", "category": "IT07 – Funktionen und Arrays", "files": [{ "name": "it07-06-zweidimensionales-array.ino", "path": "examples/IT07/it07-06-zweidimensionales-array.ino" }], "main": "it07-06-zweidimensionales-array.ino" }, + { + "id": "it07-07-array-an-funktion", + "title": "IT07.7 – Ein Array an eine Funktion übergeben", + "category": "IT07 – Funktionen und Arrays", + "files": [{ "name": "it07-07-array-an-funktion.ino", "path": "examples/IT07/it07-07-array-an-funktion.ino" }], + "main": "it07-07-array-an-funktion.ino" + }, { "id": "it08-01-struct", - "title": "IT08.1 – Einfache Struktur", + "title": "IT08.1 – Unterschiedliche Daten in einer Struktur zusammenfassen", "category": "IT08 – Strukturen und Organisation", "files": [{ "name": "it08-01-struct.ino", "path": "examples/IT08/it08-01-struct.ino" }], "main": "it08-01-struct.ino" }, { - "id": "it08-02-array-aus-structs", - "title": "IT08.2 – Array aus Strukturen", + "id": "it08-02-struct-uebergeben", + "title": "IT08.2 – Eine Struktur als Wert oder als Zeiger übergeben", "category": "IT08 – Strukturen und Organisation", - "files": [{ "name": "it08-02-array-aus-structs.ino", "path": "examples/IT08/it08-02-array-aus-structs.ino" }], - "main": "it08-02-array-aus-structs.ino" + "files": [{ "name": "it08-02-struct-uebergeben.ino", "path": "examples/IT08/it08-02-struct-uebergeben.ino" }], + "main": "it08-02-struct-uebergeben.ino" }, { - "id": "it08-03-struct-als-parameter", - "title": "IT08.3 – Struktur als Funktionsparameter", + "id": "it08-03-array-aus-structs", + "title": "IT08.3 – Mehrere Messstellen in einem Array aus Strukturen", "category": "IT08 – Strukturen und Organisation", - "files": [{ "name": "it08-03-struct-als-parameter.ino", "path": "examples/IT08/it08-03-struct-als-parameter.ino" }], - "main": "it08-03-struct-als-parameter.ino" + "files": [{ "name": "it08-03-array-aus-structs.ino", "path": "examples/IT08/it08-03-array-aus-structs.ino" }], + "main": "it08-03-array-aus-structs.ino" }, { - "id": "it08-04-funktion-auslagern", - "title": "IT08.4 – Funktion in Headerdatei auslagern", + "id": "it08-04-typedef", + "title": "IT08.4 – Mit typedef einen lesbaren Datentyp festlegen", + "category": "IT08 – Strukturen und Organisation", + "files": [{ "name": "it08-04-typedef.ino", "path": "examples/IT08/it08-04-typedef.ino" }], + "main": "it08-04-typedef.ino" + }, + { + "id": "it08-05-headerdatei", + "title": "IT08.5 – Struktur und Funktionen in eine Headerdatei auslagern", "category": "IT08 – Strukturen und Organisation", "files": [ - { "name": "it08-04-funktion-auslagern.ino", "path": "examples/IT08/it08-04-funktion-auslagern.ino" }, - { "name": "rechnen.h", "path": "examples/IT08/rechnen.h" } + { "name": "it08-05-headerdatei.ino", "path": "examples/IT08/it08-05-headerdatei.ino" }, + { "name": "messung.h", "path": "examples/IT08/messung.h" } ], - "main": "it08-04-funktion-auslagern.ino" + "main": "it08-05-headerdatei.ino" + }, + { + "id": "it08-06-funktion-testen", + "title": "IT08.6 – Eine Funktion mit Testfällen prüfen", + "category": "IT08 – Strukturen und Organisation", + "files": [{ "name": "it08-06-funktion-testen.ino", "path": "examples/IT08/it08-06-funktion-testen.ino" }], + "main": "it08-06-funktion-testen.ino" }, { "id": "it11-01-matrix-ausgeben", - "title": "IT11.1 – Matrix ausgeben", + "title": "IT11.1 – Eine Matrix mit einer Funktion lesbar ausgeben", "category": "IT11 – Matrizen", "files": [{ "name": "it11-01-matrix-ausgeben.ino", "path": "examples/IT11/it11-01-matrix-ausgeben.ino" }], "main": "it11-01-matrix-ausgeben.ino" }, { - "id": "it11-02-matrix-transponieren", - "title": "IT11.2 – Matrix transponieren", + "id": "it11-02-skalarmultiplikation", + "title": "IT11.2 – Funktionen auslagern und eine Matrix skalieren", "category": "IT11 – Matrizen", - "files": [{ "name": "it11-02-matrix-transponieren.ino", "path": "examples/IT11/it11-02-matrix-transponieren.ino" }], - "main": "it11-02-matrix-transponieren.ino" + "files": [ + { "name": "it11-02-skalarmultiplikation.ino", "path": "examples/IT11/it11-02-skalarmultiplikation.ino" }, + { "name": "mat_func.h", "path": "examples/IT11/mat_func.h" } + ], + "main": "it11-02-skalarmultiplikation.ino" }, { "id": "it11-03-matrizen-addieren", - "title": "IT11.3 – Matrizen addieren", + "title": "IT11.3 – Zwei Matrizen addieren", "category": "IT11 – Matrizen", - "files": [{ "name": "it11-03-matrizen-addieren.ino", "path": "examples/IT11/it11-03-matrizen-addieren.ino" }], + "files": [ + { "name": "it11-03-matrizen-addieren.ino", "path": "examples/IT11/it11-03-matrizen-addieren.ino" }, + { "name": "mat_func.h", "path": "examples/IT11/mat_func.h" } + ], "main": "it11-03-matrizen-addieren.ino" + }, + { + "id": "it11-04-matrix-transponieren", + "title": "IT11.4 – Eine Matrix transponieren", + "category": "IT11 – Matrizen", + "files": [ + { "name": "it11-04-matrix-transponieren.ino", "path": "examples/IT11/it11-04-matrix-transponieren.ino" }, + { "name": "mat_func.h", "path": "examples/IT11/mat_func.h" } + ], + "main": "it11-04-matrix-transponieren.ino" + }, + { + "id": "it11-05-matrizen-multiplizieren", + "title": "IT11.5 – Matrizen multiplizieren und die Einheitsmatrix prüfen", + "category": "IT11 – Matrizen", + "files": [ + { "name": "it11-05-matrizen-multiplizieren.ino", "path": "examples/IT11/it11-05-matrizen-multiplizieren.ino" }, + { "name": "mat_func.h", "path": "examples/IT11/mat_func.h" } + ], + "main": "it11-05-matrizen-multiplizieren.ino" } ], "tutor": { diff --git a/tutor/manifest.yaml b/tutor/manifest.yaml index b11b418..8c64f53 100644 --- a/tutor/manifest.yaml +++ b/tutor/manifest.yaml @@ -3,10 +3,10 @@ defaultStrategy: precision-policy topics: - id: variables-and-serial path: tutor/topics/variables-and-serial.yaml - sha256: 2f3efbbeb2f61a1649c45f0dd6bac78af40a1dc2747407b1c01d14ec7e35a096 + sha256: 754d14b983088570c9b29aef5a03c9d944bd093fd09c256e5c1e25ecb38ac64e - id: long-values path: tutor/topics/long-values.yaml - sha256: 57308d9a382bf0e7f65846f83189969255b8b317b9a9c876b95772e8db3b1dcb + sha256: a98a3ea60779ec0e9f7ed95709eb666deab121c698a53871fa3d3e3d69c23b4a strategies: - id: precision-policy path: tutor/strategies/precision-policy.yaml diff --git a/tutor/quality-cases.yaml b/tutor/quality-cases.yaml index 98ce857..b567ab6 100644 --- a/tutor/quality-cases.yaml +++ b/tutor/quality-cases.yaml @@ -1,12 +1,12 @@ schemaVersion: 1 cases: - id: variables-serial-positive - example: it01-01-variable-speichern + example: it01-01-variable-definieren expectedTopics: [variables-and-serial] forbiddenTopics: [long-values] - id: pwm-does-not-activate-variables example: it04-03-pwm-ausgang forbiddenTopics: [variables-and-serial, long-values] - id: long-values-positive - example: it03-05-millis + example: it03-07-zeit-messen expectedTopics: [long-values] diff --git a/tutor/topics/long-values.yaml b/tutor/topics/long-values.yaml index c34079a..f7082e7 100644 --- a/tutor/topics/long-values.yaml +++ b/tutor/topics/long-values.yaml @@ -7,7 +7,7 @@ activation: - fact: type-used values: [long] concepts: - - id: execution-structure + - id: long-value-meaning title: Long-Werte objective: Die Rolle eines long-Werts im aktuellen Sketch erklären. prerequisites: [] @@ -15,7 +15,7 @@ concepts: entry: [1, 50] transfer: [20, 80] misconceptions: - - id: long-too-small + - id: long-unbounded description: Ein long-Wert ist für jede beliebige Größe automatisch ausreichend. indicators: - id: identifies-long-value @@ -26,16 +26,16 @@ concepts: requiredIndicators: [identifies-long-value] minimumDistinctQuestionKinds: 1 recentWeakAnswersAllowed: 0 - - id: timing-interval + - id: long-value-usage title: Verwendung eines Long-Werts objective: Die Verwendung eines long-Werts als Mess- oder Zustandsinformation einordnen. - prerequisites: [execution-structure] + prerequisites: [long-value-meaning] difficulty: entry: [1, 50] transfer: [20, 80] misconceptions: - - id: long-is-only-text - description: Ein long-Wert sei nur für Text und nicht für numerische Messwerte geeignet. + - id: long-same-as-int + description: Ein langer Ganzzahltyp hat denselben Wertebereich wie int. indicators: - id: explains-long-usage description: Erklärt, warum der long-Wert zur dargestellten Information passt. @@ -47,7 +47,7 @@ concepts: recentWeakAnswersAllowed: 0 questions: - id: long-value-concept - concept: execution-structure + concept: long-value-meaning indicator: identifies-long-value kind: concept difficulty: [1, 50] @@ -56,7 +56,7 @@ questions: values: [long] text: Welche Information wird im Sketch in der betrachteten Variable mit langem Ganzzahltyp gespeichert? - id: long-value-application - concept: timing-interval + concept: long-value-usage indicator: explains-long-usage kind: application difficulty: [20, 80] @@ -65,7 +65,7 @@ questions: values: [long] text: Warum passt der für diese Variable verwendete Datentyp zur im Sketch gespeicherten Information? - id: long-value-prediction - concept: execution-structure + concept: long-value-meaning indicator: identifies-long-value kind: prediction difficulty: [20, 80] @@ -74,7 +74,7 @@ questions: values: [long] text: Wie erwartest du, dass sich der gespeicherte Wert dieser Variable beim nächsten Schleifendurchlauf verändert? - id: long-value-transfer - concept: timing-interval + concept: long-value-usage indicator: explains-long-usage kind: transfer difficulty: [20, 80] @@ -84,14 +84,14 @@ questions: text: Welche weitere zeitbezogene Information könntest du im aktuellen Sketch mit demselben Datentyp erfassen, ohne den vollständigen Code zu entwerfen? scaffolds: - id: long-value-scaffold - forConcept: timing-interval + forConcept: long-value-usage strategy: smaller-subproblem level: 1 hint: Benenne zuerst die Information, die der long-Wert im aktuellen Sketch repräsentiert. nextQuestion: long-value-application progression: - entryConcepts: [execution-structure] - preferredOrder: [execution-structure, timing-interval] + entryConcepts: [long-value-meaning] + preferredOrder: [long-value-meaning, long-value-usage] onRating: 1-2: remediate 3: clarify-same-indicator diff --git a/tutor/topics/variables-and-serial.yaml b/tutor/topics/variables-and-serial.yaml index 87571ea..7146942 100644 --- a/tutor/topics/variables-and-serial.yaml +++ b/tutor/topics/variables-and-serial.yaml @@ -16,7 +16,7 @@ concepts: transfer: [20, 80] misconceptions: - id: declaration-is-output - description: Eine Deklaration allein erzeugt noch keine Ausgabe im seriellen Monitor. + description: Schon die Deklaration einer Variablen erzeugt eine Ausgabe im seriellen Monitor. indicators: - id: relates-value-to-use description: Ordnet einen Variablenwert seiner Verwendung im Sketch zu. @@ -35,7 +35,7 @@ concepts: transfer: [20, 80] misconceptions: - id: print-is-storage - description: Serial.print speichert den Wert nicht dauerhaft, sondern überträgt ihn zur Ausgabe. + description: Serial.print speichert den Wert dauerhaft, statt ihn nur zur Ausgabe zu übertragen. indicators: - id: explains-serial-output description: Erklärt die Rolle eines Serial-Ausgabeaufrufs.