diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json index b58b52d5..56fae136 100644 --- a/frontend/public/components-metadata.json +++ b/frontend/public/components-metadata.json @@ -1624,6 +1624,52 @@ "8" ] }, + { + "id": "ky-040", + "tagName": "wokwi-ky-040", + "name": "KY-040 Rotary Encoder", + "category": "input", + "description": "KY-040 incremental rotary encoder with push-button. Turning the knob outputs quadrature pulses on CLK and DT (direction = which leads); the shaft press drives an active-low SW. Reads from any board's GPIO.", + "thumbnail": "\n \n \n \n \n \n \n \n \n \n \n ", + "properties": [ + { + "name": "angle", + "type": "string", + "defaultValue": 0, + "control": "text" + }, + { + "name": "stepSize", + "type": "string", + "defaultValue": 18, + "control": "text" + }, + { + "name": "pressed", + "type": "string", + "defaultValue": false, + "control": "text" + } + ], + "defaultValues": { + "angle": 0, + "stepSize": 18, + "pressed": false + }, + "pinCount": 0, + "tags": [ + "rotary", + "encoder", + "rotary encoder", + "rotary-encoder", + "ky-040", + "ky040", + "knob", + "quadrature", + "dial", + "input" + ] + }, { "id": "membrane-keypad", "tagName": "wokwi-membrane-keypad", @@ -2742,45 +2788,6 @@ "dc5" ] }, - { - "id": "ky-040", - "tagName": "wokwi-ky-040", - "name": "KY040", - "category": "other", - "thumbnail": "\n \n \n KY-040\n \n ", - "properties": [ - { - "name": "angle", - "type": "string", - "defaultValue": 0, - "control": "text" - }, - { - "name": "stepSize", - "type": "string", - "defaultValue": 18, - "control": "text" - }, - { - "name": "pressed", - "type": "string", - "defaultValue": false, - "control": "text" - } - ], - "defaultValues": { - "angle": 0, - "stepSize": 18, - "pressed": false - }, - "pinCount": 0, - "tags": [ - "ky-040", - "ky040", - "ky", - "040" - ] - }, { "id": "led-ring", "tagName": "wokwi-led-ring", diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index 9ac4014d..6742ff1e 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -84,6 +84,65 @@ export interface ExampleProject { } const legacyExamples: ExampleProject[] = [ + { + id: 'ky-040-rotary-encoder', + title: 'KY-040 Rotary Encoder', + description: + 'Read a KY-040 rotary encoder with an Arduino Uno. Turn the knob to move a counter (CLK/DT quadrature) and press the shaft (SW) to reset it. Open the Serial Monitor to watch the position.', + category: 'basics', + difficulty: 'beginner', + boardType: 'arduino-uno', + boardFilter: 'arduino-uno', + code: `// KY-040 rotary encoder — turn the knob to move the counter, +// press the shaft (SW) to reset. The encoder drives CLK/DT in quadrature; +// we sample DT on each CLK rising edge to get the direction. +const int PIN_CLK = 2; // CLK (A) +const int PIN_DT = 3; // DT (B) +const int PIN_SW = 4; // SW (push button, active LOW) + +int counter = 0; +int lastClk = HIGH; + +void setup() { + pinMode(PIN_CLK, INPUT_PULLUP); + pinMode(PIN_DT, INPUT_PULLUP); + pinMode(PIN_SW, INPUT_PULLUP); + Serial.begin(115200); + Serial.println("KY-040 rotary encoder ready"); + lastClk = digitalRead(PIN_CLK); +} + +void loop() { + int clk = digitalRead(PIN_CLK); + if (clk != lastClk && clk == HIGH) { // CLK rising edge + if (digitalRead(PIN_DT) == LOW) { + counter++; // DT LOW -> clockwise + } else { + counter--; // DT HIGH -> counter-clockwise + } + Serial.print("position: "); + Serial.println(counter); + } + lastClk = clk; + + if (digitalRead(PIN_SW) == LOW) { + counter = 0; + Serial.println("button pressed -> reset"); + delay(200); // simple debounce + } +}`, + components: [ + { type: 'wokwi-ky-040', id: 'enc1', x: 360, y: 90, properties: {} }, + ], + wires: [ + { id: 'w-enc-clk', start: { componentId: 'enc1', pinName: 'CLK' }, end: { componentId: 'arduino-uno', pinName: '2' }, color: '#f59e0b' }, + { id: 'w-enc-dt', start: { componentId: 'enc1', pinName: 'DT' }, end: { componentId: 'arduino-uno', pinName: '3' }, color: '#10b981' }, + { id: 'w-enc-sw', start: { componentId: 'enc1', pinName: 'SW' }, end: { componentId: 'arduino-uno', pinName: '4' }, color: '#3b82f6' }, + { id: 'w-enc-vcc', start: { componentId: 'enc1', pinName: 'VCC' }, end: { componentId: 'arduino-uno', pinName: '5V' }, color: '#ef4444' }, + { id: 'w-enc-gnd', start: { componentId: 'enc1', pinName: 'GND' }, end: { componentId: 'arduino-uno', pinName: 'GND' }, color: '#1f2937' }, + ], + tags: ['rotary', 'encoder', 'ky-040', 'input', 'knob', 'arduino'], + }, { id: 'stm32-bluepill-blink', title: 'STM32 Blue Pill Blink', diff --git a/scripts/component-overrides.json b/scripts/component-overrides.json index 5dfc5466..da7ca3c3 100644 --- a/scripts/component-overrides.json +++ b/scripts/component-overrides.json @@ -2164,5 +2164,12 @@ "inductor": { "name": "Inductor (custom)", "thumbnail": "\n \n \n ?mH" + }, + "ky-040": { + "name": "KY-040 Rotary Encoder", + "category": "input", + "description": "KY-040 incremental rotary encoder with push-button. Turning the knob outputs quadrature pulses on CLK and DT (direction = which leads); the shaft press drives an active-low SW. Reads from any board's GPIO.", + "tags": ["rotary", "encoder", "rotary encoder", "rotary-encoder", "ky-040", "ky040", "knob", "quadrature", "dial", "input"], + "thumbnail": "\n \n \n \n \n \n \n \n \n \n \n " } } diff --git a/scripts/generate-component-metadata.ts b/scripts/generate-component-metadata.ts index 4c6b2c6a..4a858f33 100644 --- a/scripts/generate-component-metadata.ts +++ b/scripts/generate-component-metadata.ts @@ -252,6 +252,21 @@ class MetadataGenerator { if (typeof ov.thumbnail === 'string') { comp.thumbnail = ov.thumbnail; } + // Discoverability fixes for scanned wokwi parts. Auto-generated tags and + // the default 'other' category use the original class name, so a part + // like "KY040" stays unfindable when a user searches "rotary"/"encoder" + // and sits in the wrong picker tab. Let an override patch the fields that + // ComponentRegistry.search() (name/id/description/tags) and the category + // tab actually use. + if (typeof ov.category === 'string') { + comp.category = ov.category; + } + if (typeof ov.description === 'string') { + comp.description = ov.description; + } + if (Array.isArray(ov.tags)) { + comp.tags = ov.tags; + } applied++; console.log(` 🔧 Applied overrides for ${comp.id}`);