diff --git a/frontend/src/data/examples-robot-desktop.ts b/frontend/src/data/examples-robot-desktop.ts new file mode 100644 index 00000000..b23b1002 --- /dev/null +++ b/frontend/src/data/examples-robot-desktop.ts @@ -0,0 +1,157 @@ +/** + * robot_desktop — animated-eyes desktop robot on ESP32. + * + * Real-world project from github.com/davidmonterocrespo24/robot_desktop: + * ESP32 + SSD1306 128×64 OLED + DHT11 (temp/hum) + PIR (motion) + + * LDR (ambient light) + sound sensor + 2 servos for eyebrows. + * + * Imported as-is — 34 C++ files, ~3.4k lines. Velxio normally + * translates examples to a single sketch, but this project's eye- + * animation engine (Eye/EyeTransition/EyeVariation/FaceBehavior/…) + * splits its responsibilities across enough classes that flattening + * would obscure the design. Multi-file is the right call here. + * + * Pin map (from Common.h, kept untouched): + * + * GPIO 15 DHT11 data + * GPIO 4 PIR motion sensor + * GPIO 2 Sound sensor (also doubles as `speakerPin` in sound.h + * — the original board wires a small piezo to the same + * line, but the simulator only models the sound input) + * GPIO 34 LDR / photoresistor (ADC1) + * GPIO 12 Right eyebrow servo + * GPIO 13 Left eyebrow servo + * GPIO 21 I²C SDA (OLED, ESP32 default) + * GPIO 22 I²C SCL (OLED, ESP32 default) + * + * The original sketch uses Arduino libraries (U8g2lib, DHT, + * ESP32Servo, Adafruit_Sensor). Library Manager auto-install + * resolves the first three on compile; Adafruit_Sensor is a + * transitive dep of DHT and ships alongside it. + */ + +import type { ExampleProject } from './examples'; + +const ESP32 = { type: 'wokwi-esp32-devkit-v1', id: 'esp32', x: 60, y: 40, properties: {} }; + +function w( + id: string, + from: [string, string], + to: [string, string], + color = '#00aaff', +) { + return { + id, + start: { componentId: from[0], pinName: from[1] }, + end: { componentId: to[0], pinName: to[1] }, + color, + }; +} + +export const robotDesktopExamples: ExampleProject[] = [ + { + id: 'robot-desktop-eyes', + title: 'Desktop Robot — Animated Eyes', + description: + 'Cozmo-style desktop robot on ESP32: OLED face with random blinks/looks/expressions, ' + + 'DHT11 weather mode, PIR-triggered wakeup, LDR sleep when dark, sound-triggered reactions ' + + 'and two eyebrow servos. 34-file C++ project, real Arduino libraries (U8g2lib, DHT, ESP32Servo).', + category: 'displays', + difficulty: 'advanced', + boardType: 'esp32', + boardFilter: 'esp32', + tags: [ + 'esp32', 'oled', 'ssd1306', 'dht11', 'pir', 'ldr', 'servo', + 'animation', 'robot', 'cozmo', 'eyes', 'multi-file', + ], + code: '// Entry point lives in esp32-eyes.ino — see the files panel.', + files: [ + // Generated from the upstream repo via + // python -c 'import json,glob; ...' < (each file) + // Order: .ino first (Arduino convention: the entry sketch is + // the file that matches the directory name), then headers + // alphabetical, then .hpp, then .cpp. Tabs left untouched so + // the editor matches the upstream source byte-for-byte. + { name: "esp32-eyes.ino", content: "#include \n#include \n#include \n#include \n\n#include \"Face.h\"\n#include \"Weather.h\"\n#include \"Common.h\"\n#include \"SensorDriver.h\"\n#include \"ServoMotorDriver.h\"\n// GLOBALS\nFace *face;\n\nunsigned long lastMovementTime = 0; // Tiempo del \u00faltimo movimiento detectado\nconst unsigned long WEATHER_TIMEOUT = 600000; // 10 minutos en milisegundos\nconst unsigned long SLEEP_TIMEOUT = 3600000; // 1 hora en milisegundos\n\nint progress = 0;\n\n// Flags para controlar el estado\nbool weatherTimeoutReached = false;\nbool sleepTimeoutReached = false;\n\nunsigned long lastTriggerTime = 0;\nbool lastStateMovement = false;\nbool movementDetected = false;\n\nRobotState lastInsertedState = SLEEP;\n// Estado actual\nRobotState currentState = SLEEP;\n\nTaskHandle_t faceHandle;\nServo ServoDerecha;\nServo ServoIzquierda;\n\nTaskHandle_t servoHandle;\n// GLOBALS\n\neEmotions previousEmotion;\n\nTaskHandle_t soundHandle;\n\nTaskHandle_t sensorHandle;\n\nvoid vUpdateFaceTask(void *pvParameters)\n{\n while (1)\n {\n\n face->RandomBehavior = false;\n face->RandomBlink = true;\n face->RandomLook = true;\n\n if (currentState == NORMAL)\n {\n Serial.println(\"STATE Normal 224****************************************\");\n face->RandomBehavior = true;\n }\n else if (currentState == SOUND)\n {\n // Acciones para el estado NERVIOSO\n eEmotions cEmotion = face->Behavior.CurrentEmotion;\n if (cEmotion != eEmotions::Surprised)\n {\n face->Expression.GoTo_Surprised();\n }\n Serial.println(\"STATE = SONIDO ..... *************************************************\");\n }\n else if (currentState == MOVE)\n {\n // Acciones para el estado MOVE\n Serial.println(\"STATE = MOVE ...... *************************************************\");\n eEmotions cEmotion = face->Behavior.CurrentEmotion;\n if (cEmotion != eEmotions::Focused)\n {\n face->Expression.GoTo_Focused();\n }\n }\n else if (currentState == SLEEP)\n {\n Serial.println(\"STATE = SLEEP ..... **************************************************\");\n face->RandomBehavior = false;\n face->RandomBlink = false;\n face->RandomLook = false;\n Serial.println(face->Behavior.CurrentEmotion);\n Serial.println(eEmotions::Sleepy);\n if (face->Behavior.CurrentEmotion != eEmotions::Sleepy)\n {\n face->Behavior.CurrentEmotion = eEmotions::Sleepy;\n face->Expression.GoTo_Sleepy();\n Serial.println(\"GoTo_Sleepy\");\n }\n else\n {\n Serial.println(\"Distinto ******------------------------------------------------------------------------------************\");\n face->Update();\n }\n }\n else if (currentState == WEATHER)\n {\n\n Serial.println(\"STATE = Weather ..... ************************************************\");\n Serial.println(analogRead(LDR_PIN));\n face->Expression.GoTo_Normal();\n updateWEATHER();\n vTaskDelay(pdMS_TO_TICKS(100));\n continue;\n }\n else if (currentState == INICIO)\n {\n currentState = SLEEP;\n }\n\n face->Update();\n vTaskDelay(pdMS_TO_TICKS(100));\n }\n}\n\nvoid setup(void)\n{\n // Create a serial connection\n Serial.begin(115200);\n Serial.println(\"Iniciando................\");\n ServoDerecha.write(0);\n ServoDerecha.write(90);\n\n pinMode(PIR_PIN, INPUT);\n // pinMode(SOUND_PIN, INPUT);\n pinMode(LDR_PIN, INPUT);\n // insertState(INICIO);\n currentState = SLEEP;\n lastStateMovement = digitalRead(PIR_PIN);\n // Configure input pins\n pinMode(SOUND_PIN, OUTPUT);\n randomSeed(analogRead(0));\n\n // StarWarsPlay();\n // Create a new face\n\n face = new Face(/* screenWidth = */ 128, /* screenHeight = */ 64, /* eyeSize = */ 40);\n\n initDht();\n\n delay(3000);\n // Assign the current expression\n face->Expression.GoTo_Sleepy();\n Serial.println(\"GoTo_Sleepy\");\n\n // Assign a weight to each emotion\n face->Behavior.SetEmotion(eEmotions::Normal, 1.0);\n face->Behavior.SetEmotion(eEmotions::Angry, 1.0);\n face->Behavior.SetEmotion(eEmotions::Glee, 1.0);\n face->Behavior.SetEmotion(eEmotions::Happy, 1.0);\n face->Behavior.SetEmotion(eEmotions::Sad, 1.0);\n face->Behavior.SetEmotion(eEmotions::Worried, 1.0);\n face->Behavior.SetEmotion(eEmotions::Focused, 1.0);\n face->Behavior.SetEmotion(eEmotions::Annoyed, 1.0);\n face->Behavior.SetEmotion(eEmotions::Surprised, 1.0);\n face->Behavior.SetEmotion(eEmotions::Skeptic, 1.0);\n face->Behavior.SetEmotion(eEmotions::Frustrated, 1.0);\n face->Behavior.SetEmotion(eEmotions::Unimpressed, 1.0);\n face->Behavior.SetEmotion(eEmotions::Sleepy, 1.0);\n face->Behavior.SetEmotion(eEmotions::Suspicious, 1.0);\n face->Behavior.SetEmotion(eEmotions::Squint, 1.0);\n face->Behavior.SetEmotion(eEmotions::Furious, 1.0);\n face->Behavior.SetEmotion(eEmotions::Scared, 1.0);\n face->Behavior.SetEmotion(eEmotions::Awe, 1.0);\n\n face->RandomBehavior = false;\n\n // Automatically blink\n face->RandomBlink = true;\n // Set blink rate\n face->Blink.Timer.SetIntervalMillis(8000);\n face->Behavior.Timer.SetIntervalMillis(15000);\n\n // Automatically choose a new random direction to look\n face->RandomLook = true;\n ServoDerecha.attach(SERVO1_Derecha);\n ServoIzquierda.attach(SERVO2_Izquierda);\n ServoIzquierda.write(0);\n ServoDerecha.write(170);\n\n delay(3000);\n\n xTaskCreatePinnedToCore(\n vUpdateFaceTask, /* Task function. */\n \"Update face\", /* name of task. */\n 10000, /* Stack size of task */\n NULL, /* parameter of the task */\n 1, /* priority of the task */\n &faceHandle, /* Task handle to keep track of created task */\n 0); /* pin task to core 0 */\n\n xTaskCreatePinnedToCore(\n vMoveServoTask, /* Task function. */\n \"Update Servo\", /* name of task. */\n 10000, /* Stack size of task */\n face, /* parameter of the task */\n 5, /* priority of the task */\n &servoHandle, /* Task handle to keep track of created task */\n 1); /* pin task to core 0 */\n\n xTaskCreatePinnedToCore(\n vReadSensorTask, /* Task function. */\n \"Read Sensor\", /* name of task. */\n 10000, /* Stack size of task */\n NULL, /* parameter of the task */\n 10, /* priority of the task */\n &sensorHandle, /* Task handle to keep track of created task */\n 1); /* pin task to core 0 */\n\n /* xTaskCreatePinnedToCore(\n vSoundTask, /* Task function. */\n // \"Sound\", /* name of task. */\n // 10000, /* Stack size of task */\n // NULL, /* parameter of the task */\n // 10, /* priority of the task */\n // &sensorHandle, /* Task handle to keep track of created task */\n // 1); /* pin task to core 0 */\n\n Serial.println(\"Finalizando MAIN\");\n}\n\nvoid loop()\n{\n // vUpdateFaceTask();\n}\n" }, + { name: "Animations.h", content: "/***************************************************\nCopyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n\nclass IAnimation {\npublic:\n\tvirtual float GetValue() = 0;\n\tvirtual float GetValue(unsigned long overWriteMillis) = 0;\n\tvirtual unsigned long GetElapsed() = 0;\n\nprivate:\n\tvirtual float Calculate(unsigned long elapsedMillis) = 0;\n};\n\nclass AnimationBase : IAnimation {\n public:\n\t AnimationBase(unsigned long interval) : Interval(interval), StarTime(millis()) {}\n\n\t unsigned long Interval;\n\t unsigned long StarTime;\n\n\t virtual void Restart() {\n\t\t StarTime = millis();\n\t }\n\t float GetValue() override final {\n\t\t return GetValue(GetElapsed());\n\t }\n\t float GetValue(unsigned long elapsedMillis) override final {\n\t\t return Calculate(elapsedMillis);\n\t }\n\t unsigned long GetElapsed() override {\n\t\t return static_cast (millis() - StarTime);\n\t }\n\n protected:\n\t float Calculate(unsigned long elapsedMillis) override { return 0.0; }\n};\n\nclass DeltaAnimation : public AnimationBase {\n public:\n\t unsigned long StarTime;\n\t DeltaAnimation(unsigned long interval) : AnimationBase(interval) {};\n\t float Calculate(unsigned long elapsedMillis) {\n\t\t if (elapsedMillis < Interval)\t{\n\t\t\t return 0.0f;\n\t\t }\n\t\t else {\n\t\t\t return 1.0f;\n\t\t }\n\t };\n};\n\n\nclass StepAnimation : public AnimationBase {\n public:\n\t unsigned long Interval;\n\t unsigned long StarTime;\n\t bool IsActive = true;\n \tStepAnimation(unsigned long interval) : AnimationBase(interval) {};\n\n\t float Calculate(unsigned long elapsedMillis) {\n\t\t if (elapsedMillis < Interval)\t{\n\t\t\t return 0.0f;\n\t\t }\n\t\t return 1.0f;\n\t };\n};\n\n\nclass RampAnimation : public AnimationBase {\npublic:\n\n\tunsigned long StarTime;\n\tbool IsActive = true;\n\n\tRampAnimation(unsigned long interval) : AnimationBase(interval) {};\n\n\tfloat Calculate(unsigned long elapsedMillis) {\n\t\tif (elapsedMillis < Interval)\t{\n\t\t\treturn static_cast(elapsedMillis) / Interval;\n\t\t}\n\t\treturn 1.0f;\n\t};\n};\n\nclass TriangleAnimation : public AnimationBase {\npublic:\n\n\tTriangleAnimation(unsigned long interval) : AnimationBase(interval) {\n\t\t_t0 = interval / 2;\n\t\t_t1 = interval - _t0;\n\t}\n\tTriangleAnimation(unsigned long t0, unsigned long t1) : AnimationBase(t0 + t1) {\n\t\t_t0 = t0;\n\t\t_t1 = t1;\n\t};\n\tfloat Calculate(unsigned long elapsedMillis) {\n\t\tif (elapsedMillis % Interval < _t0) {\n\t\t\treturn static_cast(elapsedMillis % Interval) / _t0;\n\t\t}\n\t\treturn 1.0f - (static_cast(elapsedMillis % Interval) - _t0) / _t1;\n\t};\n\tunsigned long _t0;\n\tunsigned long _t1;\n};\n\n\nclass TrapeziumAnimation : public AnimationBase {\npublic:\n\tTrapeziumAnimation(unsigned long t) : AnimationBase(t) {\n\t\t_t0 = t / 3;\n\t\t_t1 = _t0;\n\t\t_t2 = t - _t0 - _t1;\n\t};\n\tTrapeziumAnimation(unsigned long t0, unsigned long t1, unsigned long t2) : AnimationBase(t0 + t1 + t2) {\n\t\t_t0 = t0;\n\t\t_t1 = t1;\n\t\t_t2 = t2;\n\t}; \n\tfloat Calculate(unsigned long elapsedMillis) override {\n\t\tif (elapsedMillis > Interval) return 0.0;\n\t\tif (elapsedMillis < _t0) {\n\t\t\treturn static_cast(elapsedMillis) / _t0;\n\t\t}\n\t\telse if (elapsedMillis < _t0 + _t1) {\n\t\t\treturn 1.0f;\n\t\t}\n\t\telse {\n\t\t\treturn 1.0f - (static_cast(elapsedMillis) - _t1 - _t0) / _t2;\n\t\t}\n\t};\n\n\tunsigned long _t0;\n\tunsigned long _t1;\n\tunsigned long _t2;\n};\n\n\nclass TrapeziumPulseAnimation : public AnimationBase {\npublic:\n\tTrapeziumPulseAnimation(unsigned long t) : AnimationBase(t) {\n\t\t_t0 = 0;\n\t\t_t1 = t / 3;\n\t\t_t2 = t - _t0 - _t0;\n\t\t_t3 = _t1;\n\t\t_t4 = 0;\n\t};\n\n\tTrapeziumPulseAnimation(unsigned long t0, unsigned long t1, unsigned long t2) : AnimationBase(t0 + t1 + t2) {\n\t\t_t0 = 0;\n\t\t_t1 = t0;\n\t\t_t2 = t1;\n\t\t_t3 = t2;\n\t\t_t4 = 0;\n\t};\n\n\tTrapeziumPulseAnimation(unsigned long t0, unsigned long t1, unsigned long t2, unsigned long t3, unsigned long t4) : AnimationBase(t0 + t1 + t2 + t3 + t4) {\n\t\t_t0 = t0;\n\t\t_t1 = t1;\n\t\t_t2 = t2;\n\t\t_t3 = t3;\n\t\t_t4 = t4;\n\t};\n\n\tfloat Calculate(unsigned long elapsedMillis) override {\n\t\tunsigned long elapsed = elapsedMillis % Interval;\n\n\t\tif (elapsed < _t0) {\n\t\t\treturn 0.0;\n\t\t}\n\t\tif (elapsed < _t0 + _t1) {\n\t\t\treturn static_cast(elapsed - _t0) / _t1;\n\t\t}\n\t\telse if (elapsed < _t0 + _t1 + _t2)\t{\n\t\t\treturn 1.0f;\n\t\t}\n\t\telse if (elapsed < _t0 + _t1 + _t2 + _t3)\t{\n\t\t\treturn 1.0f - (static_cast(elapsed) - _t2 - _t1 - _t0) / _t3;\n\t\t}\n\t\treturn 0.0;\n\t};\n\n\tvoid SetInterval(uint16_t t) {\n\t\t_t0 = 0;\n\t\t_t1 = t / 3;\n\t\t_t2 = t - _t0 - _t0;\n\t\t_t3 = _t1;\n\t\t_t4 = 0;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\n\tvoid SetTriangle(uint16_t t, uint16_t delay) {\n\t\t_t0 = 0;\n\t\t_t1 = t / 2;\n\t\t_t2 = 0;\n\t\t_t3 = _t1;\n\t\t_t4 = delay;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\n\tvoid SetTriangleCuadrature(uint16_t t, uint16_t delay) {\n\t\t_t0 = delay;\n\t\t_t1 = t / 2;\n\t\t_t2 = 0;\n\t\t_t3 = _t1;\n\t\t_t4 = 0;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\t\n\tvoid SetPulse(uint16_t t, uint16_t delay) {\n\t\t_t0 = 0;\n\t\t_t1 = t / 3;\n\t\t_t2 = t - _t0 - _t0;\n\t\t_t3 = _t1;\n\t\t_t4 = delay;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\n\tvoid SetPulseCuadrature(uint16_t t, uint16_t delay) {\n\t\t_t0 = delay;\n\t\t_t1 = t / 3;\n\t\t_t2 = t - _t0 - _t0;\n\t\t_t3 = _t1;\n\t\t_t4 = 0;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\n\tvoid SetInterval(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3, uint16_t t4) {\n\t\t_t0 = t0;\n\t\t_t1 = t1;\n\t\t_t2 = t2;\n\t\t_t3 = t3;\n\t\t_t4 = t4;\n\t\tInterval = _t0 + _t1 + _t2 + _t3 + _t4;\n\t}\n\n\tunsigned long _t0;\n\tunsigned long _t1;\n\tunsigned long _t2;\n\tunsigned long _t3;\n\tunsigned long _t4;\n};\n\n#endif" }, + { name: "AsyncTimer.h", content: "/***************************************************\nCopyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n\ntypedef void(*AsyncTimerCallback)();\n\nclass AsyncTimer {\n public:\n\tAsyncTimer(unsigned long millisInterval);\n\tAsyncTimer(unsigned long millisInterval, AsyncTimerCallback OnFinish);\n\n\tvoid Start();\n\tvoid Reset();\n\tvoid Stop();\n\tbool Update();\n\n\tvoid SetIntervalMillis(unsigned long interval);\n\t\n\tunsigned long GetStartTime();\n\tunsigned long GetElapsedTime();\n\tunsigned long GetRemainingTime();\n\n\tbool IsActive() const;\n\tbool IsExpired() const;\n\t\n\tunsigned long Interval;\n\t\n\tAsyncTimerCallback OnFinish;\n\nprivate:\n\tbool _isActive;\n\tbool _isExpired;\n\tunsigned long _startTime;\n};\n#endif" }, + { name: "BlinkAssistant.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Animations.h\"\n#include \"AsyncTimer.h\"\n\nclass Face;\n\nclass BlinkAssistant {\n protected:\n\tFace& _face;\n\n public:\n\tBlinkAssistant(Face& face);\n\n\tAsyncTimer Timer;\n\n\tvoid Update();\n\tvoid Blink();\n};\n\n#endif" }, + { name: "Common.h", content: "#ifndef COMMON_h\n#define COMMON_h\n#include \n#include \nextern U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2;\n\n#define DHTPIN 15 // Pin de datos del sensor DHT11\n#define DHTTYPE DHT11 // Tipo de sensor DHT (puedes cambiar a DHT22 si es necesario)\n\n#define SERVO1_Derecha 12\n#define SERVO2_Izquierda 13\n#define PIR_PIN 4\n#define SOUND_PIN 2\n#define LDR_PIN 34\n\n\n enum RobotState\n{\n INICIO,\n NORMAL,\n SOUND,\n MOVE,\n SLEEP,\n WEATHER\n};\n\nextern TaskHandle_t faceHandle;\nextern Servo ServoDerecha;\nextern Servo ServoIzquierda;\n\nextern TaskHandle_t servoHandle;\n\n\n\nextern TaskHandle_t soundHandle;\n\nextern TaskHandle_t sensorHandle;\n\n#define speakerPin 2\n\n#define MAX_STATES 100 // N\u00famero m\u00e1ximo de estados en la cola\n\n\nextern RobotState stateQueue[MAX_STATES]; // Cola de estados\n//extern int queueFront = 0; // Frente de la cola\n//extern int queueRear = -1; // Final de la cola\n//extern int queueItemCount = 0; // N\u00famero de elementos en la cola\n\nextern unsigned long lastMovementTime; // Tiempo del \u00faltimo movimiento detectado\nextern const unsigned long WEATHER_TIMEOUT; // 10 minutos en milisegundos\nextern const unsigned long SLEEP_TIMEOUT ; // 1 hora en milisegundos\n\nextern unsigned long lastTriggerTime;\nextern bool lastStateMovement;\nextern bool movementDetected;\n\n// Flags para controlar el estado\nextern bool weatherTimeoutReached;\nextern bool sleepTimeoutReached ;\n\n// Funci\u00f3n para insertar un estado en la cola\nextern RobotState lastInsertedState; // Variable para almacenar el \u00faltimo estado insertado\n// Estado actual\nextern RobotState currentState; // Variable para almacenar el \u00faltimo estado insertado\n\nextern bool isLightOn();\nextern bool isMovementDetected();\n\n\n#endif" }, + { name: "Eye.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Common.h\"\n#include \"Animations.h\"\n#include \"EyeConfig.h\"\n#include \"EyeDrawer.h\"\n#include \"EyeTransition.h\"\n#include \"EyeTransformation.h\"\n#include \"EyeVariation.h\"\n#include \"EyeBlink.h\"\n#include \"EyeVariation.h\"\n\nclass Face;\n\nclass Eye {\n protected:\n Face& _face;\n\n void Update();\n void ChainOperators();\n\n public:\n Eye(Face& face);\n\n uint16_t CenterX;\n uint16_t CenterY;\n bool IsMirrored = false;\n\n EyeConfig Config;\n EyeConfig* FinalConfig;\n\n EyeTransition Transition;\n EyeTransformation Transformation;\n EyeVariation Variation1;\n EyeVariation Variation2;\n EyeBlink BlinkTransformation;\n\n void ApplyPreset(const EyeConfig preset);\n void TransitionTo(const EyeConfig preset);\n void Draw();\n};\n\n#endif" }, + { name: "EyeBlink.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Animations.h\"\n#include \"EyeConfig.h\"\n\nclass EyeBlink {\n protected:\n\npublic:\n\tEyeBlink();\n\n\tEyeConfig* Input;\n\tEyeConfig Output;\n\n\tTrapeziumAnimation Animation;\n\n\tint32_t BlinkWidth = 60;\n\tint32_t BlinkHeight = 2;\n\n\tvoid Update();\n\tvoid Apply(float t);\n};\n\n#endif" }, + { name: "EyeConfig.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \nstruct EyeConfig\n{\n\tint16_t OffsetX;\n\tint16_t OffsetY;\n\n \tint16_t Height;\n\tint16_t Width;\n\n\tfloat Slope_Top;\n\tfloat Slope_Bottom;\n\n\tint16_t Radius_Top;\n\tint16_t Radius_Bottom;\n\n\tint16_t Inverse_Radius_Top;\n\tint16_t Inverse_Radius_Bottom;\n\n\tint16_t Inverse_Offset_Top;\n\tint16_t Inverse_Offset_Bottom;\n};\n\n#endif" }, + { name: "EyeDrawer.h", content: "/***************************************************\nCopyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Common.h\"\n#include \"EyeConfig.h\"\n\nenum CornerType {T_R, T_L, B_L, B_R};\n\n/**\n * Contains all functions to draw eye based on supplied (expression-based) config\n */\nclass EyeDrawer {\n public:\n static void Draw(int16_t centerX, int16_t centerY, EyeConfig *config) {\n // Amount by which corners will be shifted up/down based on requested \"slope\"\n int32_t delta_y_top = config->Height * config->Slope_Top / 2.0;\n int32_t delta_y_bottom = config->Height * config->Slope_Bottom / 2.0;\n // Full extent of the eye, after accounting for slope added at top and bottom\n auto totalHeight = config->Height + delta_y_top - delta_y_bottom;\n // If the requested top/bottom radius would exceed the height of the eye, adjust them downwards \n if (config->Radius_Bottom > 0 && config->Radius_Top > 0 && totalHeight - 1 < config->Radius_Bottom + config->Radius_Top) {\n int32_t corrected_radius_top = (float)config->Radius_Top * (totalHeight - 1) / (config->Radius_Bottom + config->Radius_Top);\n int32_t corrected_radius_bottom = (float)config->Radius_Bottom * (totalHeight - 1) / (config->Radius_Bottom + config->Radius_Top);\n config->Radius_Top = corrected_radius_top;\n config->Radius_Bottom = corrected_radius_bottom;\n }\n\n // Calculate _inside_ corners of eye (TL, TR, BL, and BR) before any slope or rounded corners are applied\n int32_t TLc_y = centerY + config->OffsetY - config->Height/2 + config->Radius_Top - delta_y_top;\n int32_t TLc_x = centerX + config->OffsetX - config->Width/2 + config->Radius_Top;\n int32_t TRc_y = centerY + config->OffsetY - config->Height/2 + config->Radius_Top + delta_y_top;\n int32_t TRc_x = centerX + config->OffsetX + config->Width/2 - config->Radius_Top;\n int32_t BLc_y = centerY + config->OffsetY + config->Height/2 - config->Radius_Bottom - delta_y_bottom;\n int32_t BLc_x = centerX + config->OffsetX - config->Width/2 + config->Radius_Bottom;\n int32_t BRc_y = centerY + config->OffsetY + config->Height/2 - config->Radius_Bottom + delta_y_bottom;\n int32_t BRc_x = centerX + config->OffsetX + config->Width/2 - config->Radius_Bottom;\n \n // Calculate interior extents\n int32_t min_c_x = min(TLc_x, BLc_x);\n int32_t max_c_x = max(TRc_x, BRc_x);\n int32_t min_c_y = min(TLc_y, TRc_y);\n int32_t max_c_y = max(BLc_y, BRc_y);\n\n // Fill eye centre\n EyeDrawer::FillRectangle(min_c_x, min_c_y, max_c_x, max_c_y, 1);\n\n // Fill eye outwards to meet edges of rounded corners \n EyeDrawer::FillRectangle(TRc_x, TRc_y, BRc_x + config->Radius_Bottom, BRc_y, 1); // Right\n\t\t EyeDrawer::FillRectangle(TLc_x - config->Radius_Top, TLc_y, BLc_x, BLc_y, 1); // Left\n\t\t EyeDrawer::FillRectangle(TLc_x, TLc_y - config->Radius_Top, TRc_x, TRc_y, 1); // Top\n\t\t EyeDrawer::FillRectangle(BLc_x, BLc_y, BRc_x, BRc_y + config->Radius_Bottom, 1); // Bottom\n \n // Draw slanted edges at top of bottom of eyes \n // +ve Slope_Top means eyes slope downwards towards middle of face\n if(config->Slope_Top > 0) {\n EyeDrawer::FillRectangularTriangle(TLc_x, TLc_y-config->Radius_Top, TRc_x, TRc_y-config->Radius_Top, 0);\n EyeDrawer::FillRectangularTriangle(TRc_x, TRc_y-config->Radius_Top, TLc_x, TLc_y-config->Radius_Top, 1);\n } \n else if(config->Slope_Top < 0) {\n EyeDrawer::FillRectangularTriangle(TRc_x, TRc_y-config->Radius_Top, TLc_x, TLc_y-config->Radius_Top, 0);\n EyeDrawer::FillRectangularTriangle(TLc_x, TLc_y-config->Radius_Top, TRc_x, TRc_y-config->Radius_Top, 1);\n }\n // Draw slanted edges at bottom of eyes\n if(config->Slope_Bottom > 0) {\n EyeDrawer::FillRectangularTriangle(BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, 0);\n EyeDrawer::FillRectangularTriangle(BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, 1);\n }\n else if (config->Slope_Bottom < 0) {\n EyeDrawer::FillRectangularTriangle(BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, 0);\n EyeDrawer::FillRectangularTriangle(BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, 1);\n }\n\n // Draw corners (which extend \"outwards\" towards corner of screen from supplied coordinate values)\n if(config->Radius_Top > 0) {\n EyeDrawer::FillEllipseCorner(T_L, TLc_x, TLc_y, config->Radius_Top, config->Radius_Top, 1);\n EyeDrawer::FillEllipseCorner(T_R, TRc_x, TRc_y, config->Radius_Top, config->Radius_Top, 1);\n }\n if(config->Radius_Bottom > 0) {\n EyeDrawer::FillEllipseCorner(B_L, BLc_x, BLc_y, config->Radius_Bottom, config->Radius_Bottom, 1);\n EyeDrawer::FillEllipseCorner(B_R, BRc_x, BRc_y, config->Radius_Bottom, config->Radius_Bottom, 1);\n }\n }\n\n // Draw rounded corners\n static void FillEllipseCorner(CornerType corner, int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) {\n if (rx < 2) return;\n if (ry < 2) return;\n int32_t x, y;\n int32_t rx2 = rx * rx;\n int32_t ry2 = ry * ry;\n int32_t fx2 = 4 * rx2;\n int32_t fy2 = 4 * ry2;\n int32_t s;\n\n if (corner == T_R) {\n for(x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {\n u8g2.drawHLine(x0, y0 - y, x);\n if(s >= 0) {\n s += fx2 * (1 - y);\n y--;\n }\n s += ry2 * ((4 * x) + 6);\n } \n for(x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {\n u8g2.drawHLine(x0, y0 - y, x);\n if (s >= 0) {\n s += fy2 * (1 - x);\n x--;\n }\n s += rx2 * ((4 * y) + 6);\n }\n }\n\n else if (corner == B_R) {\n for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {\n u8g2.drawHLine(x0, y0 + y -1, x);\n if (s >= 0) {\n s += fx2 * (1 - y);\n y--;\n }\n s += ry2 * ((4 * x) + 6);\n }\n for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {\n u8g2.drawHLine(x0, y0 + y -1, x);\n if (s >= 0) {\n s += fy2 * (1 - x);\n x--;\n }\n s += rx2 * ((4 * y) + 6);\n }\n }\n\n else if (corner == T_L) {\n for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {\n u8g2.drawHLine(x0-x, y0 - y, x);\n if (s >= 0) {\n s += fx2 * (1 - y);\n y--;\n }\n s += ry2 * ((4 * x) + 6);\n }\n for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {\n u8g2.drawHLine(x0-x, y0 - y, x);\n if (s >= 0) {\n s += fy2 * (1 - x);\n x--;\n }\n s += rx2 * ((4 * y) + 6);\n }\n }\n\n else if (corner == B_L) {\n for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {\n u8g2.drawHLine(x0-x, y0 + y - 1, x);\n if (s >= 0) {\n s += fx2 * (1 - y);\n y--;\n }\n s += ry2 * ((4 * x) + 6);\n }\n for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {\n u8g2.drawHLine(x0-x, y0 + y , x);\n if (s >= 0) {\n s += fy2 * (1 - x);\n x--;\n }\n s += rx2 * ((4 * y) + 6);\n }\n }\n }\n\n // Fill a solid rectangle between specified coordinates\n static void FillRectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t color) {\n // Always draw from TL->BR\n int32_t l = min(x0, x1);\n int32_t r = max(x0, x1);\n int32_t t = min(y0, y1);\n int32_t b = max(y0, y1);\n int32_t w = r-l;\n int32_t h = b-t; \n u8g2.setDrawColor(color);\n u8g2.drawBox(l, t, w, h);\n u8g2.setDrawColor(1);\n }\n\n static void FillRectangularTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t color) {\n u8g2.setDrawColor(color);\n u8g2.drawTriangle(x0, y0, x1, y1, x1, y0);\n u8g2.setDrawColor(1);\n }\n\n static void FillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t color) {\n u8g2.setDrawColor(color);\n u8g2.drawTriangle(x0, y0, x1, y1, x2, y2);\n u8g2.setDrawColor(1);\n }\n};\n\n#endif" }, + { name: "EyePresets.h", content: "/***************************************************\nCopyright (c) 2023 Alastair Aitchison, Playful Technology, 2020 Luis Llamas (www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"EyeConfig.h\"\n\nstatic const EyeConfig Preset_Normal = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 40,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 8,\n\t.Radius_Bottom = 8,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Happy = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 10,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 10,\n\t.Radius_Bottom = 0,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Glee = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 8,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 8,\n\t.Radius_Bottom = 0,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 5,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Sad = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 15,\n\t.Width = 40,\n\t.Slope_Top = -0.5,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 1,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Worried = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 25,\n\t.Width = 40,\n\t.Slope_Top = -0.1,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 6,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Worried_Alt = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 35,\n\t.Width = 40,\n\t.Slope_Top = -0.2,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 6,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Focused = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 14,\n\t.Width = 40,\n\t.Slope_Top = 0.2,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 3,\n\t.Radius_Bottom = 1,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Annoyed = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 12,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 0,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Annoyed_Alt = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 5,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 0,\n\t.Radius_Bottom = 4,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Surprised = {\n\t.OffsetX = -2,\n\t.OffsetY = 0,\n\t.Height = 45,\n\t.Width = 45,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 16,\n\t.Radius_Bottom = 16,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Skeptic = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 40,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 10,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Skeptic_Alt = {\n\t.OffsetX = 0,\n\t.OffsetY = -6,\n\t.Height = 26,\n\t.Width = 40,\n\t.Slope_Top = 0.3,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 1,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Frustrated = {\n\t.OffsetX = 3,\n\t.OffsetY = -5,\n\t.Height = 12,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 0,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Unimpressed = {\n\t.OffsetX = 3,\n\t.OffsetY = 0,\n\t.Height = 12,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 1,\n\t.Radius_Bottom = 10,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Unimpressed_Alt = {\n\t.OffsetX = 3,\n\t.OffsetY = -3,\n\t.Height = 22,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 1,\n\t.Radius_Bottom = 16,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Sleepy = {\n\t.OffsetX = 0,\n\t.OffsetY = -2,\n\t.Height = 14,\n\t.Width = 40,\n\t.Slope_Top = -0.5,\n\t.Slope_Bottom = -0.5,\n\t.Radius_Top = 3,\n\t.Radius_Bottom = 3,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Sleepy_Alt = {\n\t.OffsetX = 0,\n\t.OffsetY = -2,\n\t.Height = 8,\n\t.Width = 40,\n\t.Slope_Top = -0.5,\n\t.Slope_Bottom = -0.5,\n\t.Radius_Top = 3,\n\t.Radius_Bottom = 3,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Suspicious = {\n\t.OffsetX = 0,\n\t.OffsetY = 0,\n\t.Height = 22,\n\t.Width = 40,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 8,\n\t.Radius_Bottom = 3,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Suspicious_Alt = {\n\t.OffsetX = 0,\n\t.OffsetY = -3,\n\t.Height = 16,\n\t.Width = 40,\n\t.Slope_Top = 0.2,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 6,\n\t.Radius_Bottom = 3,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Squint = {\n\t.OffsetX = -10,\n\t.OffsetY = -3,\n\t.Height = 35,\n\t.Width = 35,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 8,\n\t.Radius_Bottom = 8,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Squint_Alt = {\n\t.OffsetX = 5,\n\t.OffsetY = 0,\n\t.Height = 20,\n\t.Width = 20,\n\t.Slope_Top = 0,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 5,\n\t.Radius_Bottom = 5,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Angry = {\n\t.OffsetX = -3,\n\t.OffsetY = 0,\n\t.Height = 20,\n\t.Width = 40,\n\t.Slope_Top = 0.3,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 2,\n\t.Radius_Bottom = 12,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Furious = {\n\t.OffsetX = -2,\n\t.OffsetY = 0,\n\t.Height = 30,\n\t.Width = 40,\n\t.Slope_Top = 0.4,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 2,\n\t.Radius_Bottom = 8,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Scared = {\n\t.OffsetX = -3,\n\t.OffsetY = 0,\n\t.Height = 40,\n\t.Width = 40,\n\t.Slope_Top = -0.1,\n\t.Slope_Bottom = 0,\n\t.Radius_Top = 12,\n\t.Radius_Bottom = 8,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\nstatic const EyeConfig Preset_Awe = {\n\t.OffsetX = 2,\n\t.OffsetY = 0,\n\t.Height = 35,\n\t.Width = 45,\n\t.Slope_Top = -0.1,\n\t.Slope_Bottom = 0.1,\n\t.Radius_Top = 12,\n\t.Radius_Bottom = 12,\n\t.Inverse_Radius_Top = 0,\n\t.Inverse_Radius_Bottom = 0,\n\t.Inverse_Offset_Top = 0,\n\t.Inverse_Offset_Bottom = 0\n};\n\n#endif" }, + { name: "EyeTransformation.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Animations.h\"\n#include \"EyeConfig.h\"\n\nstruct Transformation\n{\n\tfloat MoveX = 0.0;\n\tfloat MoveY = 0.0;\n\tfloat ScaleX = 1.0;\n\tfloat ScaleY = 1.0;\n};\n\nclass EyeTransformation\n{\npublic:\n\tEyeTransformation();\n\n\tEyeConfig* Input;\n\tEyeConfig Output;\n\n\tTransformation Origin;\n\tTransformation Current;\n\tTransformation Destin;\n\n\tRampAnimation Animation;\n\n\tvoid Update();\n\tvoid Apply();\n\tvoid SetDestin(Transformation transformation);\n};\n\n#endif\n\n" }, + { name: "EyeTransition.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Animations.h\"\n#include \"EyeConfig.h\"\n\nclass EyeTransition {\npublic:\n\tEyeTransition();\n\n\tEyeConfig* Origin;\n\tEyeConfig Destin;\n\n\tRampAnimation Animation;\n\n\tvoid Update();\n\tvoid Apply(float t);\n};\n\n#endif\n\n" }, + { name: "EyeVariation.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Animations.h\"\n#include \"EyeConfig.h\"\n\nclass EyeVariation {\npublic:\n\tEyeVariation();\n\n\tEyeConfig* Input;\n\tEyeConfig Output;\n\n\tTrapeziumPulseAnimation Animation;\n\n\tEyeConfig Values;\n\tvoid Clear();\n\n\tvoid SetInterval(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3, uint16_t t4);\n\n\tvoid Update();\n\tvoid Apply(float t);\n};\n\n#endif" }, + { name: "Face.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"Common.h\"\n#include \"Animations.h\"\n#include \"EyePresets.h\"\n#include \"Eye.h\"\n#include \"FaceExpression.h\"\n#include \"FaceBehavior.h\"\n#include \"LookAssistant.h\"\n#include \"BlinkAssistant.h\"\n\nclass Face {\n\npublic:\n Face(uint16_t screenWidth, uint16_t screenHeight, uint16_t eyeSize);\n\n uint16_t Width;\n uint16_t Height;\n uint16_t CenterX;\n uint16_t CenterY;\n uint16_t EyeSize;\n uint16_t EyeInterDistance = 4;\n\n Eye LeftEye;\n Eye RightEye;\n BlinkAssistant Blink;\n LookAssistant Look;\n FaceBehavior Behavior;\n FaceExpression Expression;\n\n void Update();\n void DoBlink();\n\n bool RandomBehavior = true;\n bool RandomLook = true;\n bool RandomBlink = true;\n\n void LookLeft();\n void LookRight();\n void LookFront();\n void LookTop();\n void LookBottom();\n void Wait(unsigned long milliseconds);\n\nprotected:\n void Draw();\n};\n\n#endif" }, + { name: "FaceBehavior.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"FaceEmotions.hpp\"\n#include \"AsyncTimer.h\"\n\nclass Face;\n\nclass FaceBehavior\n{\n protected:\n\tFace& _face;\n\n public:\n\tFaceBehavior(Face& face);\n\n\teEmotions CurrentEmotion;\n\n\tfloat Emotions[eEmotions::EMOTIONS_COUNT];\n\n\tAsyncTimer Timer;\n\n\tvoid SetEmotion(eEmotions emotion, float value);\n\tfloat GetEmotion(eEmotions emotion);\n\n\tvoid Clear();\n\tvoid Update();\n\teEmotions GetRandomEmotion();\n\n\tvoid GoToEmotion(eEmotions emotion);\n};\n\n#endif\n\n" }, + { name: "FaceExpression.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n\nclass Face;\n\nclass FaceExpression {\n protected:\n Face& _face;\n\n public:\n FaceExpression(Face& face);\n\n void ClearVariations();\n\n void GoTo_Normal();\n void GoTo_Angry();\n void GoTo_Glee();\n void GoTo_Happy();\n void GoTo_Sad();\n void GoTo_Worried();\n void GoTo_Focused();\n void GoTo_Annoyed();\n void GoTo_Surprised();\n void GoTo_Skeptic();\n void GoTo_Frustrated();\n void GoTo_Unimpressed();\n void GoTo_Sleepy();\n void GoTo_Suspicious();\n void GoTo_Squint();\n void GoTo_Furious();\n void GoTo_Scared();\n void GoTo_Awe();\n};\n\n#endif" }, + { name: "LookAssistant.h", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n#include \"EyeTransformation.h\"\n#include \"AsyncTimer.h\"\n\nclass Face;\n\nclass LookAssistant\n{\n protected:\n\tFace& _face;\n\n public:\n\tLookAssistant(Face& face);\n\n\tTransformation transformation;\n\n\tAsyncTimer Timer;\n\n\tvoid LookAt(float x, float y);\n\tvoid Update();\n};\n\n#endif\n\n" }, + { name: "SensorDriver.h", content: "#ifndef _SENSORDRIVER_h\n#define _SENSORDRIVER_h\n#include \"Common.h\"\n\nint minLuz = 10; // Valor inicial negativo para indicar que no se ha le\u00eddo a\u00fan\nint maxLuz = 10;\nint umbralLuz = 0;\nunsigned long tiempoUltimaLectura = 0;\nconst unsigned long intervaloAjuste = 10000; // 20 segundos recalcula el umbral\n\nvoid ajustarUmbral() {\n umbralLuz = (minLuz + maxLuz) / 2;\n Serial.print(\"Nuevo umbral de luz calculado: \");\n Serial.println(umbralLuz);\n}\n\nbool isLightOn() {\n int luz = analogRead(LDR_PIN);\n if (luz < minLuz) minLuz = luz;\n if (luz > maxLuz) maxLuz = luz;\n ajustarUmbral(); \n bool hayLuz = (luz < umbralLuz); \n if (!hayLuz) {\n Serial.print(\"No hay Luz: \" );\n Serial.println(luz);\n } else {\n Serial.print(\"Hay Luz: \");\n Serial.println(luz);\n }\n return hayLuz;\n}\n\nbool isMovementDetected()\n{\n int currentStateMovement = digitalRead(PIR_PIN);\n\n if (currentStateMovement != lastStateMovement)\n {\n lastTriggerTime = millis(); // Actualiza el tiempo del \u00faltimo cambio de estado\n lastStateMovement = currentStateMovement; // Actualiza el \u00faltimo estado registrado\n movementDetected = true; // Indica que se ha detectado un cambio de estado\n }\n\n // Verifica si ha pasado menos de 5 segundos desde el \u00faltimo cambio de estado\n if (movementDetected && millis() - lastTriggerTime < 3000)\n {\n Serial.println(\"Movimiento DETECTADO..... 130\");\n return true; // Retorna true si se detect\u00f3 un cambio de estado en menos de 5 segundos\n }\n\n // Verifica si han pasado m\u00e1s de 5 segundos desde el \u00faltimo cambio de estado\n if (millis() - lastTriggerTime >= 3000)\n {\n movementDetected = false; // Reinicia la bandera de detecci\u00f3n de cambio de estado\n }\n\n return false; // Si no se cumple ninguna condici\u00f3n, retorna false\n}\n\nbool isSoundDetected()\n{\n int sonido = digitalRead(SOUND_PIN);\n if (sonido == HIGH)\n {\n Serial.println(\"Sonido Detectado\");\n return true;\n }\n else\n {\n return false;\n }\n}\n\nvoid vReadSensorTask(void *pvParameters)\n{\n Serial.println(\"INICIO DE LA LECTURA DE SENSORES\");\n int lightSignalCount = 0;\n unsigned long lastLightSignalTime = 0;\n bool isLightOnPreviously = false;\n\n while (1)\n {\n int STATE = currentState;\n\n if (isMovementDetected() && STATE == SLEEP)\n {\n // insertState(NORMAL);\n currentState = NORMAL;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>>MOVIMIENTO DETECTADO AND STATE=SLEEP 171\");\n lastMovementTime = millis(); // Actualizar el tiempo del \u00faltimo movimiento detectado\n sleepTimeoutReached = false; // Reiniciar la bandera del timeout de sue\u00f1o\n weatherTimeoutReached = false;\n }\n else if (isMovementDetected() && STATE == NORMAL)\n {\n // insertState(MOVE);\n currentState = MOVE;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>>>MOVIMIENTO DETECTADO AND STATE == NORMAL 179\");\n lastMovementTime = millis(); // Actualizar el tiempo del \u00faltimo movimiento detectado\n sleepTimeoutReached = false; // Reiniciar la bandera del timeout de sue\u00f1o\n weatherTimeoutReached = false;\n }\n else if (isMovementDetected() && STATE == WEATHER)\n {\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>>>>> MOVIMIENTO DETECTADO AND STATE == WEATHER 93\");\n lastMovementTime = millis(); // Actualizar el tiempo del \u00faltimo movimiento detectado\n sleepTimeoutReached = false; // Reiniciar la bandera del timeout de sue\u00f1o\n weatherTimeoutReached = false;\n currentState = NORMAL;\n }\n // L\u00f3gica adicional para cambiar al estado WEATHER despu\u00e9s de 10 minutos sin movimiento\n if (millis() - lastMovementTime >= WEATHER_TIMEOUT && !weatherTimeoutReached)\n {\n // insertState(WEATHER);\n currentState = WEATHER;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>>WEATHER despu\u00e9s 10 minutos sin movimiento\");\n weatherTimeoutReached = true; // Marcar que se alcanz\u00f3 el tiempo l\u00edmite para el estado WEATHER\n }\n\n // L\u00f3gica adicional para cambiar al estado SLEEP despu\u00e9s de una hora sin movimiento\n if (millis() - lastMovementTime >= SLEEP_TIMEOUT && !sleepTimeoutReached)\n {\n // insertState(SLEEP);\n currentState = SLEEP;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>SLEEP despu\u00e9s de una hora sin movimiento\");\n sleepTimeoutReached = true; // Marcar que se alcanz\u00f3 el tiempo l\u00edmite para el estado SLEEP\n }\n\n if (STATE == MOVE && millis() - lastMovementTime >= 30000)\n {\n currentState = NORMAL;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>>VOLVIENDO A NORMAL despu\u00e9s de 30 segundos en MOVE\");\n }\n\n // L\u00f3gica adicional para cambiar al estado WEATHER si se recibe y deja de recibir se\u00f1al de luz 3 veces\n if (!isLightOn())\n {\n // insertState(SLEEP);\n currentState = SLEEP;\n Serial.println(\"NO HAY LUZ --> SLEEP 160\");\n }\n\n if (isLightOn())\n {\n if (!isLightOnPreviously)\n {\n lightSignalCount++;\n isLightOnPreviously = true;\n Serial.println(\"Se\u00f1al de luz recibida\");\n }\n }\n else\n {\n isLightOnPreviously = false;\n }\n\n if (lightSignalCount >= 3)\n {\n currentState = WEATHER;\n Serial.println(\">>>>>>>>>>>>>>>>>>>>> Cambiando a WEATHER despu\u00e9s de recibir y dejar de recibir se\u00f1al de luz 3 veces\");\n lightSignalCount = 0; // Reiniciar el contador\n }\n\n vTaskDelay(pdMS_TO_TICKS(500)); // Espera antes de volver a verificar los sensores\n }\n}\n\n#endif" }, + { name: "ServoMotorDriver.h", content: "\n#ifndef _SERVOMOTORDRIVER_h\n#define _SERVOMOTORDRIVER_h\n#include \"Common.h\"\n\n#include \"Face.h\"\n\nvoid moveServoWithPID(Servo servo, int fromAngle, int toAngle)\n{\n servo.write(fromAngle);\n vTaskDelay(pdMS_TO_TICKS(105));\n servo.write(toAngle);\n}\n\nvoid vMoveServoTask(void *pvParameters)\n{\n Face *face = (Face *)pvParameters; // Convertir el puntero gen\u00e9rico a Face\n while (1)\n {\n Serial.println(\"DRIVER Motor\");\n Serial.println(face->Behavior.CurrentEmotion);\n if (currentState == MOVE || currentState == NORMAL)\n {\n switch (face->Behavior.CurrentEmotion)\n {\n\n case eEmotions::Angry:\n Serial.println(\"Angry Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(90);\n ServoIzquierda.write(90);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(90);\n ServoIzquierda.write(0);\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(90);\n ServoIzquierda.write(90);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(90);\n ServoIzquierda.write(0);\n break;\n\n case eEmotions::Happy:\n Serial.println(\"Happy Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(45);\n ServoIzquierda.write(170);\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(45);\n ServoIzquierda.write(170);\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(45);\n ServoIzquierda.write(170);\n break;\n\n case eEmotions::Surprised:\n Serial.println(\"Surprised Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(60);\n ServoIzquierda.write(0);\n break;\n\n case eEmotions::Sad:\n Serial.println(\"Sad Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(90);\n ServoIzquierda.write(90);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(90);\n ServoIzquierda.write(0);\n break;\n\n case eEmotions::Worried:\n Serial.println(\"Worried Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(90);\n ServoIzquierda.write(90);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(90);\n ServoIzquierda.write(0);\n break;\n\n case eEmotions::Suspicious:\n Serial.println(\"Suspicious Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(60);\n ServoIzquierda.write(0);\n break;\n\n case eEmotions::Furious:\n Serial.println(\"Furious Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(45);\n ServoIzquierda.write(170);\n break;\n\n case eEmotions::Scared:\n Serial.println(\"Scared Motor\");\n ServoDerecha.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n vTaskDelay(pdMS_TO_TICKS(505));\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(1000));\n ServoDerecha.write(60);\n ServoIzquierda.write(0);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(45);\n ServoIzquierda.write(45);\n vTaskDelay(pdMS_TO_TICKS(505));\n\n ServoDerecha.write(60);\n ServoIzquierda.write(60);\n break;\n\n default:\n break;\n }\n }\n\n vTaskDelay(pdMS_TO_TICKS(13000));\n }\n}\n\n#endif" }, + { name: "Watchdog.h", content: "#ifndef WATCHDOG_H\n#define WATCHDOG_H\n\n\n#include // Agregar la biblioteca para el watchdog\n\nvoid feedWatchdog() {\n esp_task_wdt_reset();\n}\n\n#endif" }, + { name: "Weather.h", content: "#ifndef _WEATHER_h\n#define _WEATHER_h\n\n#include \n\n#define DHTPIN 15 // Pin de datos del sensor DHT11\n#define DHTTYPE DHT11 // Tipo de sensor DHT (puedes cambiar a DHT22 si es necesario)\n#define SUN 0\n#define SUN_CLOUD 1\n#define CLOUD 2\n#define RAIN 3\n#define THUNDER 4\nDHT dht(DHTPIN, DHTTYPE);\n\nvoid initDht()\n{\n dht.begin();\n}\n\n\n\nvoid drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)\n{\n // fonts used:\n // u8g2_font_open_iconic_embedded_6x_t\n // u8g2_font_open_iconic_weather_6x_t\n // encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic\n\n switch (symbol)\n {\n case SUN:\n u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);\n u8g2.drawGlyph(x, y, 69);\n break;\n case SUN_CLOUD:\n u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);\n u8g2.drawGlyph(x, y, 65);\n break;\n case CLOUD:\n u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);\n u8g2.drawGlyph(x, y, 64);\n break;\n case RAIN:\n u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);\n u8g2.drawGlyph(x, y, 67);\n break;\n case THUNDER:\n u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);\n u8g2.drawGlyph(x, y, 67);\n break;\n }\n}\nvoid drawWeather(uint8_t symbol, int degree)\n{\n drawWeatherSymbol(0, 48, symbol);\n u8g2.setFont(u8g2_font_logisoso32_tf);\n u8g2.setCursor(48 + 3, 42);\n u8g2.print(degree);\n u8g2.print(\" C\"); // requires enableUTF8Print()\n \n}\n/*\n Draw a string with specified pixel offset.\n The offset can be negative.\n Limitation: The monochrome font with 8 pixel per glyph\n*/\nvoid drawScrollString(int16_t offset, const char *s)\n{\n static char buf[36]; // should for screen with up to 256 pixel width\n size_t len;\n size_t char_offset = 0;\n u8g2_uint_t dx = 0;\n size_t visible = 0;\n len = strlen(s);\n if (offset < 0)\n {\n char_offset = (-offset) / 8;\n dx = offset + char_offset * 8;\n if (char_offset >= u8g2.getDisplayWidth() / 8)\n return;\n visible = u8g2.getDisplayWidth() / 8 - char_offset + 1;\n strncpy(buf, s, visible);\n buf[visible] = '\\0';\n u8g2.setFont(u8g2_font_8x13_mf);\n u8g2.drawStr(char_offset * 8 - dx, 62, buf);\n }\n else\n {\n char_offset = offset / 8;\n if (char_offset >= len)\n return; // nothing visible\n dx = offset - char_offset * 8;\n visible = len - char_offset;\n if (visible > u8g2.getDisplayWidth() / 8 + 1)\n visible = u8g2.getDisplayWidth() / 8 + 1;\n strncpy(buf, s + char_offset, visible);\n buf[visible] = '\\0';\n u8g2.setFont(u8g2_font_8x13_mf);\n u8g2.drawStr(-dx, 62, buf);\n }\n}\nvoid draw(const char *s, uint8_t symbol, int degree)\n{\n int16_t offset = -(int16_t)u8g2.getDisplayWidth();\n int16_t len = strlen(s);\n for (;;)\n {\n u8g2.firstPage();\n do\n {\n drawWeather(symbol, degree);\n drawScrollString(offset, s);\n } while (u8g2.nextPage());\n delay(20);\n offset += 2;\n if (offset > len * 8 + 1)\n break;\n }\n}\n\n\nvoid updateWEATHER() {\n float humidity = dht.readHumidity();\n float temperature = dht.readTemperature();\n\n char scrollText[15];\n sprintf(scrollText, \"Humidity:%3d%%\",(int)humidity);\n\n if (temperature >= 25 && humidity >= 70) {\n draw(scrollText, THUNDER,temperature);\n } else if (temperature >= 20 && humidity >= 60) {\n draw(scrollText, RAIN,temperature);\n } else if (temperature >= 15 && humidity >= 50) {\n draw(scrollText, RAIN, temperature);\n } else if (temperature >= 10 && humidity >= 40) {\n draw(scrollText, SUN_CLOUD,temperature);\n } else if (temperature < 10 && humidity < 40) {\n draw(scrollText, CLOUD, temperature);\n } else {\n draw(scrollText, SUN, temperature);\n }\n}\n\n#endif\n" }, + { name: "sound.h", content: "#ifndef _SOUND_h\n#define _SOUND_h\n\n/***************************************************\n Project R2D2 Sound Generator\n\n To all fans of StarWars and Arduino!\n\n Written by Marcelo Larios\n\n BSD license, all text above must be included in any redistribution\n\n Participated in the Instructable Arduino Contest 2019\n https://www.instructables.com/R2D2-Sound-Generator/\n****************************************************/\n\n\n#define speakerPin 2\nint freq = 2000;\nint channel = 0;\nint resolution = 8;\n\n\n#define C0 16.35\n#define Db0 17.32\n#define D0 18.35\n#define Eb0 19.45\n#define E0 20.60\n#define F0 21.83\n#define Gb0 23.12\n#define G0 24.50\n#define Ab0 25.96\n#define LA0 27.50\n#define Bb0 29.14\n#define B0 30.87\n#define C1 32.70\n#define Db1 34.65\n#define D1 36.71\n#define Eb1 38.89\n#define E1 41.20\n#define F1 43.65\n#define Gb1 46.25\n#define G1 49.00\n#define Ab1 51.91\n#define LA1 55.00\n#define Bb1 58.27\n#define B1 61.74\n#define C2 65.41\n#define Db2 69.30\n#define D2 73.42\n#define Eb2 77.78\n#define E2 82.41\n#define F2 87.31\n#define Gb2 92.50\n#define G2 98.00\n#define Ab2 103.83\n#define LA2 110.00\n#define Bb2 116.54\n#define B2 123.47\n#define C3 130.81\n#define Db3 138.59\n#define D3 146.83\n#define Eb3 155.56\n#define E3 164.81\n#define F3 174.61\n#define Gb3 185.00\n#define G3 196.00\n#define Ab3 207.65\n#define LA3 220.00\n#define Bb3 233.08\n#define B3 246.94\n#define C4 261.63\n#define Db4 277.18\n#define D4 293.66\n#define Eb4 311.13\n#define E4 329.63\n#define F4 349.23\n#define Gb4 369.99\n#define G4 392.00\n#define Ab4 415.30\n#define LA4 440.00\n#define Bb4 466.16\n#define B4 493.88\n#define C5 523.25\n#define Db5 554.37\n#define D5 587.33\n#define Eb5 622.25\n#define E5 659.26\n#define F5 698.46\n#define Gb5 739.99\n#define G5 783.99\n#define Ab5 830.61\n#define LA5 880.00\n#define Bb5 932.33\n#define B5 987.77\n#define C6 1046.50\n#define Db6 1108.73\n#define D6 1174.66\n#define Eb6 1244.51\n#define E6 1318.51\n#define F6 1396.91\n#define Gb6 1479.98\n#define G6 1567.98\n#define Ab6 1661.22\n#define LA6 1760.00\n#define Bb6 1864.66\n#define B6 1975.53\n#define C7 2093.00\n#define Db7 2217.46\n#define D7 2349.32\n#define Eb7 2489.02\n#define E7 2637.02\n#define F7 2793.83\n#define Gb7 2959.96\n#define G7 3135.96\n#define Ab7 3322.44\n#define LA7 3520.01\n#define Bb7 3729.31\n#define B7 3951.07\n#define C8 4186.01\n#define Db8 4434.92\n#define D8 4698.64\n#define Eb8 4978.03\n// DURATION OF THE NOTES \n#define BPM 120 // you can change this value changing all the others\n// I think tecnically BPM supposed to be 104, but original 120\n// from instructable sounds good. 180 sounds speedy, 240 sounds mario, 300 sounds\n// middle eastern, 400 middle eastern techno pop?\n#define H 2*Q //half 2/4\n#define Q 60000/BPM //quarter 1/4 \n#define E Q/2 //eighth 1/8\n#define S Q/4 // sixteenth 1/16\n#define W 4*Q // whole 4/4\n\n\n\nvoid phrase1()\n{\n int k = random(1000, 2000);\n for (int i = 0; i <= random(100, 2000); i++)\n {\n tone(speakerPin, k + (-i * 2));\n vTaskDelay(pdMS_TO_TICKS((random(.9, 2))));\n }\n for (int i = 0; i <= random(100, 1000); i++)\n {\n tone(speakerPin, k + (i * 10));\n vTaskDelay(pdMS_TO_TICKS((random(.9, 2))));\n }\n}\n\nvoid phrase2()\n{\n\n int k = random(1000, 2000);\n for (int i = 0; i <= random(100, 2000); i++)\n {\n tone(speakerPin, k + (i * 2));\n vTaskDelay(pdMS_TO_TICKS((random(.9, 2))));\n }\n for (int i = 0; i <= random(100, 1000); i++)\n {\n tone(speakerPin, k + (-i * 10));\n vTaskDelay(pdMS_TO_TICKS((random(.9, 2))));\n }\n}\n\nvoid playerTone()\n{\n int K = 2000;\n switch (random(1, 7))\n {\n case 1:\n phrase1();\n break;\n case 2:\n phrase2();\n break;\n case 3:\n phrase1();\n phrase2();\n break;\n case 4:\n phrase1();\n phrase2();\n phrase1();\n break;\n case 5:\n phrase1();\n phrase2();\n phrase1();\n phrase2();\n phrase1();\n break;\n case 6:\n phrase2();\n phrase1();\n phrase2();\n break;\n }\n for (int i = 0; i <= random(3, 9); i++)\n {\n tone(speakerPin, K + random(-1700, 2000));\n vTaskDelay(pdMS_TO_TICKS((random(70, 170))));\n noTone(speakerPin);\n vTaskDelay(pdMS_TO_TICKS((random(0, 30))));\n }\n noTone(speakerPin);\n vTaskDelay(pdMS_TO_TICKS((random(2000, 4000))));\n}\n\n\n\nvoid mytone(int freq, int duration) {\n ledcWriteTone(channel, freq);\n vTaskDelay(pdMS_TO_TICKS((duration)));\n ledcWriteTone(channel, 0);\n}\n \nvoid StarWars() { \n ledcSetup(channel, freq, resolution);\n ledcAttachPin(speakerPin, channel);\n }\n \nvoid StarWarsPlay() {\n //mytone(note, duration)\n mytone(LA3,Q); \n mytone(LA3,Q);\n mytone(LA3,Q);\n mytone(F3,E+S);\n mytone(C4,S);\n \n mytone(LA3,Q);\n mytone(F3,E+S);\n mytone(C4,S);\n mytone(LA3,H);\n \n mytone(E4,Q); \n mytone(E4,Q);\n mytone(E4,Q);\n mytone(F4,E+S);\n mytone(C4,S);\n \n mytone(Ab3,Q);\n mytone(F3,E+S);\n mytone(C4,S);\n mytone(LA3,H);\n \n mytone(LA4,Q);\n mytone(LA3,E+S);\n mytone(LA3,S);\n mytone(LA4,Q);\n mytone(Ab4,E+S);\n mytone(G4,S);\n \n mytone(Gb4,S);\n mytone(E4,S);\n mytone(F4,E);\n vTaskDelay(pdMS_TO_TICKS((E)));//PAUSE\n mytone(Bb3,E);\n mytone(Eb4,Q);\n mytone(D4,E+S);\n mytone(Db4,S);\n \n mytone(C4,S);\n mytone(B3,S);\n mytone(C4,E);\n vTaskDelay(pdMS_TO_TICKS((E)));//PAUSE QUASI FINE RIGA\n mytone(F3,E);\n mytone(Ab3,Q);\n mytone(F3,E+S);\n mytone(LA3,S);\n \n mytone(C4,Q);\n mytone(LA3,E+S);\n mytone(C4,S);\n mytone(E4,H);\n \n mytone(LA4,Q);\n mytone(LA3,E+S);\n mytone(LA3,S);\n mytone(LA4,Q);\n mytone(Ab4,E+S);\n mytone(G4,S);\n \n mytone(Gb4,S);\n mytone(E4,S);\n mytone(F4,E);\n vTaskDelay(pdMS_TO_TICKS((E)));//PAUSE\n mytone(Bb3,E);\n mytone(Eb4,Q);\n mytone(D4,E+S);\n mytone(Db4,S);\n \n mytone(C4,S);\n mytone(B3,S);\n mytone(C4,E);\n vTaskDelay(pdMS_TO_TICKS((E)));//PAUSE QUASI FINE RIGA\n mytone(F3,E);\n mytone(Ab3,Q);\n mytone(F3,E+S);\n mytone(C4,S);\n \n mytone(LA3,Q);\n mytone(F3,E+S);\n mytone(C4,S);\n mytone(LA3,H);\n \n vTaskDelay(pdMS_TO_TICKS((2*H)));\n}\n\nvoid vSoundTask(void *pvParameters)\n{\n while (1)\n {\n playerTone();\n vTaskDelay(pdMS_TO_TICKS(random(60000, 600000)));\n }\n}\n\n\n#endif" }, + { name: "FaceEmotions.hpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \n\nenum eEmotions {\n\tNormal=0,\n\tAngry,\n\tGlee,\n\tHappy,\n\tSad,\n\tWorried,\n\tFocused,\n\tAnnoyed,\n\tSurprised,\n\tSkeptic,\n\tFrustrated,\n\tUnimpressed,\n\tSleepy,\n\tSuspicious,\n\tSquint,\n\tFurious,\n\tScared,\n\tAwe,\n\tEMOTIONS_COUNT\n};\n\n#endif\n" }, + { name: "AsyncTimer.cpp", content: "/***************************************************\nCopyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see (millis() - _startTime) >= Interval) {\n\t\t_isExpired = true;\n\t\tif (OnFinish != nullptr) OnFinish();\n\t\tReset();\n\t}\n\treturn _isExpired;\n}\n\nvoid AsyncTimer::SetIntervalMillis(unsigned long interval) {\n\tInterval = interval;\n}\n\nunsigned long AsyncTimer::GetStartTime() {\n\treturn _startTime;\n}\n\nunsigned long AsyncTimer::GetElapsedTime() {\n\treturn millis() - _startTime;\n}\n\nunsigned long AsyncTimer::GetRemainingTime() {\n\treturn Interval - millis() + _startTime;\n}\n\nbool AsyncTimer::IsActive() const {\n\treturn _isActive;\n}\n\nbool AsyncTimer::IsExpired() const{\n\treturn _isExpired;\n}" }, + { name: "BlinkAssistant.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see IsMirrored = false;\n\n\tChainOperators();\n\tVariation1.Animation._t0 = 200;\n\tVariation1.Animation._t1 = 200;\n\tVariation1.Animation._t2 = 200;\n\tVariation1.Animation._t3 = 200;\n\tVariation1.Animation._t4 = 0;\n\tVariation1.Animation.Interval = 800;\n\n\tVariation2.Animation._t0 = 0;\n\tVariation2.Animation._t1 = 200;\n\tVariation2.Animation._t2 = 200;\n\tVariation2.Animation._t3 = 200;\n\tVariation2.Animation._t4 = 200;\n\tVariation2.Animation.Interval = 800;\n}\n\nvoid Eye::ChainOperators() {\n\tTransition.Origin = &Config;\n\tTransformation.Input = &Config;\n\tVariation1.Input = &(Transformation.Output);\n\tVariation2.Input = &(Variation1.Output);\n\tBlinkTransformation.Input = &(Variation2.Output);\n\tFinalConfig = &(BlinkTransformation.Output);\n}\n\nvoid Eye::Update() {\n\tTransition.Update();\n\tTransformation.Update();\n\tVariation1.Update();\n\tVariation2.Update();\n\tBlinkTransformation.Update();\n}\n\nvoid Eye::Draw() {\n\tUpdate();\n\tEyeDrawer::Draw(CenterX, CenterY, FinalConfig);\n}\n\nvoid Eye::ApplyPreset(const EyeConfig config) {\n\tConfig.OffsetX = this->IsMirrored ? -config.OffsetX : config.OffsetX;\n\tConfig.OffsetY = -config.OffsetY;\n\tConfig.Height = config.Height;\n\tConfig.Width = config.Width;\n\tConfig.Slope_Top = this->IsMirrored ? config.Slope_Top : -config.Slope_Top;\n\tConfig.Slope_Bottom = this->IsMirrored ? config.Slope_Bottom : -config.Slope_Bottom;\n\tConfig.Radius_Top = config.Radius_Top;\n\tConfig.Radius_Bottom = config.Radius_Bottom;\n\tConfig.Inverse_Radius_Top = config.Inverse_Radius_Top;\n\tConfig.Inverse_Radius_Bottom = config.Inverse_Radius_Bottom;\n\n\tTransition.Animation.Restart();\n}\n\nvoid Eye::TransitionTo(const EyeConfig config) {\n\tTransition.Destin.OffsetX = this->IsMirrored ? -config.OffsetX : config.OffsetX;\n\tTransition.Destin.OffsetY = -config.OffsetY;\n\tTransition.Destin.Height = config.Height;\n\tTransition.Destin.Width = config.Width;\n\tTransition.Destin.Slope_Top = this->IsMirrored ? config.Slope_Top : -config.Slope_Top;\n\tTransition.Destin.Slope_Bottom = this->IsMirrored ? config.Slope_Bottom : -config.Slope_Bottom;\n\tTransition.Destin.Radius_Top = config.Radius_Top;\n\tTransition.Destin.Radius_Bottom = config.Radius_Bottom;\n\tTransition.Destin.Inverse_Radius_Top = config.Inverse_Radius_Top;\n\tTransition.Destin.Inverse_Radius_Bottom = config.Inverse_Radius_Bottom;\n\n\tTransition.Animation.Restart();\n}" }, + { name: "EyeBlink.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see Animation.Interval) t = 0.0;\n\tApply(t * t);\n}\n\n\nvoid EyeBlink::Apply(float t) {\n\tOutput.OffsetX = Input->OffsetX;\n\tOutput.OffsetY = Input->OffsetY;\n\n\tOutput.Width = (BlinkWidth - Input->Width) * t + Input->Width;\n\tOutput.Height = (BlinkHeight - Input->Height) * t + Input->Height;\n\n\tOutput.Slope_Top = Input->Slope_Top * (1.0 - t);\n\tOutput.Slope_Bottom = Input->Slope_Bottom * (1.0 - t);\n\tOutput.Radius_Top = Input->Radius_Top * (1.0 - t);\n\tOutput.Radius_Bottom = Input->Radius_Bottom * (1.0 - t);\n\tOutput.Inverse_Radius_Top = Input->Inverse_Radius_Top * (1.0 - t);\n\tOutput.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom * (1.0 - t);\n\tOutput.Inverse_Offset_Top = Input->Inverse_Offset_Top * (1.0 - t);\n\tOutput.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom * (1.0 - t);\n}\n\n" }, + { name: "EyeTransformation.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see OffsetX + Current.MoveX;\n\tOutput.OffsetY = Input->OffsetY - Current.MoveY;\n\tOutput.Width = Input->Width * Current.ScaleX;\n\tOutput.Height = Input->Height * Current.ScaleY;\n\n\tOutput.Slope_Top = Input->Slope_Top;\n\tOutput.Slope_Bottom = Input->Slope_Bottom;\n\tOutput.Radius_Top = Input->Radius_Top;\n\tOutput.Radius_Bottom = Input->Radius_Bottom;\n\tOutput.Inverse_Radius_Top = Input->Inverse_Radius_Top;\n\tOutput.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom;\n\tOutput.Inverse_Offset_Top = Input->Inverse_Offset_Top;\n\tOutput.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom;\n}\n\nvoid EyeTransformation::SetDestin(Transformation transformation)\n{\n\tOrigin.MoveX = Current.MoveX;\n\tOrigin.MoveY = Current.MoveY;\n\tOrigin.ScaleX = Current.ScaleX;\n\tOrigin.ScaleY = Current.ScaleY;\n\n\tDestin.MoveX = transformation.MoveX;\n\tDestin.MoveY = transformation.MoveY;\n\tDestin.ScaleX = transformation.ScaleX;\n\tDestin.ScaleY = transformation.ScaleY;\n}\n\n\n" }, + { name: "EyeTransition.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see OffsetX = Origin->OffsetX * (1.0 - t) + Destin.OffsetX * t;\n\tOrigin->OffsetY = Origin->OffsetY * (1.0 - t) + Destin.OffsetY * t;\n\tOrigin->Height = Origin->Height * (1.0 - t) + Destin.Height * t;\n\tOrigin->Width = Origin->Width * (1.0 - t) + Destin.Width * t;\n\tOrigin->Slope_Top = Origin->Slope_Top * (1.0 - t) + Destin.Slope_Top * t;\n\tOrigin->Slope_Bottom = Origin->Slope_Bottom * (1.0 - t) + Destin.Slope_Bottom * t;\n\tOrigin->Radius_Top = Origin->Radius_Top * (1.0 - t) + Destin.Radius_Top * t;\n\tOrigin->Radius_Bottom = Origin->Radius_Bottom * (1.0 - t) + Destin.Radius_Bottom * t;\n\tOrigin->Inverse_Radius_Top = Origin->Inverse_Radius_Top * (1.0 - t) + Destin.Inverse_Radius_Top * t;\n\tOrigin->Inverse_Radius_Bottom = Origin->Inverse_Radius_Bottom * (1.0 - t) + Destin.Inverse_Radius_Bottom * t;\n\tOrigin->Inverse_Offset_Top = Origin->Inverse_Offset_Top * (1.0 - t) + Destin.Inverse_Offset_Top * t;\n\tOrigin->Inverse_Offset_Bottom = Origin->Inverse_Offset_Bottom * (1.0 - t) + Destin.Inverse_Offset_Bottom * t;\n}" }, + { name: "EyeVariation.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see OffsetX + Values.OffsetX * t;\n\tOutput.OffsetY = Input->OffsetY + Values.OffsetY * t;\n\tOutput.Height = Input->Height + Values.Height * t;;\n\tOutput.Width = Input->Width + Values.Width * t;\n\tOutput.Slope_Top = Input->Slope_Top + Values.Slope_Top * t;\n\tOutput.Slope_Bottom = Input->Slope_Bottom + Values.Slope_Bottom * t;\n\tOutput.Radius_Top = Input->Radius_Top + Values.Radius_Top * t;\n\tOutput.Radius_Bottom = Input->Radius_Bottom + Values.Radius_Bottom * t;\n\tOutput.Inverse_Radius_Top = Input->Inverse_Radius_Top + Values.Inverse_Radius_Top * t;\n\tOutput.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom + Values.Inverse_Radius_Bottom * t;\n\tOutput.Inverse_Offset_Top = Input->Inverse_Offset_Top + Values.Inverse_Offset_Top * t;\n\tOutput.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom + Values.Inverse_Offset_Bottom * t;;\n}" }, + { name: "Face.cpp", content: "/***************************************************\nCopyright (c) 2020 Luis Llamas\n(www.luisllamas.es)\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at your option) any later version. \n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see 0 ? y : -y) * 0.4;\n\n\ttransformation.MoveX = moveX_x;\n\ttransformation.MoveY = moveY_y; //moveY_x + moveY_y;\n\ttransformation.ScaleX = 1.0;\n\ttransformation.ScaleY = scaleY_x * scaleY_y;\n\t_face.RightEye.Transformation.SetDestin(transformation);\n\n\tmoveY_x = +3 * x;\n\tscaleY_x = 1.0 + x * 0.2;\n\ttransformation.MoveX = moveX_x;\n\ttransformation.MoveY = + moveY_y; //moveY_x + moveY_y;\n\ttransformation.ScaleX = 1.0;\n\ttransformation.ScaleY = scaleY_x * scaleY_y;\n\t_face.LeftEye.Transformation.SetDestin(transformation);\n\n\t_face.RightEye.Transformation.Animation.Restart();\n\t_face.LeftEye.Transformation.Animation.Restart();\n}\n\nvoid LookAssistant::Update() {\n\tTimer.Update();\n\n\tif (Timer.IsExpired()) {\n\t\tTimer.Reset();\n\t\tauto x = random(-70, 70);\n\t\tauto y = random(-70, 70);\n\t\tLookAt((float)x / 100, (float)y / 100);\n\t}\n\n}\n" }, + ], + components: [ + ESP32, + { type: 'wokwi-ssd1306', id: 'oled', x: 380, y: 40, properties: { protocol: 'i2c' } }, + { type: 'wokwi-dht22', id: 'dht', x: 380, y: 240, properties: {} }, + { type: 'wokwi-pir-motion-sensor', id: 'pir', x: 380, y: 360, properties: {} }, + { type: 'wokwi-big-sound-sensor', id: 'sound', x: 540, y: 360, properties: {} }, + { type: 'wokwi-photoresistor-sensor', id: 'ldr', x: 540, y: 240, properties: { lux: '500' } }, + { type: 'wokwi-servo', id: 'servo-r', x: 700, y: 40, properties: {} }, + { type: 'wokwi-servo', id: 'servo-l', x: 700, y: 180, properties: {} }, + ], + wires: [ + // ── Power rail: every sensor + OLED + servos pulls 3V3 / GND + // off the ESP32. The two servos run off 3V3 too because the + // simulator doesn't model the brown-out a real SG90 would + // cause — on real hardware they'd need an external 5V. + w('p-oled-vcc', ['esp32', '3V3'], ['oled', 'VCC'], '#ff0000'), + w('p-oled-gnd', ['oled', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-dht-vcc', ['esp32', '3V3'], ['dht', 'VCC'], '#ff0000'), + w('p-dht-gnd', ['dht', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-pir-vcc', ['esp32', '3V3'], ['pir', 'VCC'], '#ff0000'), + w('p-pir-gnd', ['pir', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-snd-vcc', ['esp32', '3V3'], ['sound', 'VCC'], '#ff0000'), + w('p-snd-gnd', ['sound', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-ldr-vcc', ['esp32', '3V3'], ['ldr', 'VCC'], '#ff0000'), + w('p-ldr-gnd', ['ldr', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-sr-vcc', ['esp32', '3V3'], ['servo-r', 'V+'], '#ff0000'), + w('p-sr-gnd', ['servo-r', 'GND'], ['esp32', 'GND'], '#000000'), + w('p-sl-vcc', ['esp32', '3V3'], ['servo-l', 'V+'], '#ff0000'), + w('p-sl-gnd', ['servo-l', 'GND'], ['esp32', 'GND'], '#000000'), + + // ── I²C bus: OLED on the ESP32's default I²C (SDA=21, SCL=22). + // U8G2_SSD1306_128X64_NONAME_F_HW_I2C in the sketch uses + // Wire.begin() which defaults to these pins. + w('i2c-sda', ['esp32', '21'], ['oled', 'SDA'], '#ffd000'), + w('i2c-scl', ['esp32', '22'], ['oled', 'SCL'], '#00d0ff'), + + // ── Sensor signal lines, matching Common.h's #define block. + w('s-dht', ['dht', 'SDA'], ['esp32', '15'], '#00ffaa'), + w('s-pir', ['pir', 'OUT'], ['esp32', '4'], '#ff00ff'), + w('s-snd', ['sound', 'OUT'], ['esp32', '2'], '#ffaa00'), + w('s-ldr', ['ldr', 'AO'], ['esp32', '34'], '#aaff00'), + + // ── Servo signal lines (PWM via LEDC on the ESP32). + w('s-sr', ['servo-r', 'PWM'], ['esp32', '12'], '#ffffff'), + w('s-sl', ['servo-l', 'PWM'], ['esp32', '13'], '#dddddd'), + ], + }, +]; diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index ceb8ed6d..86043e71 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -11,6 +11,7 @@ import { hundredDaysExamples } from './examples-100-days'; import { picowWifiExamples } from './examples-picow-wifi'; import { epaperExamples } from './examples-displays-epaper'; import { retroIntelExamples } from './examples-retro-intel'; +import { robotDesktopExamples } from './examples-robot-desktop'; /** Per-board setup for multi-board examples */ export interface ExampleBoard { @@ -7652,6 +7653,7 @@ export const exampleProjects: ExampleProject[] = [ ...picowWifiExamples, ...epaperExamples, ...retroIntelExamples, + ...robotDesktopExamples, ]; // Get examples by category