├── .gitignore ├── CMakeLists.txt ├── CircuitOS.ino ├── Jenkinsfile ├── LICENSE ├── Makefile ├── README.md ├── Setup.hpp ├── examples ├── ListMenu │ └── ListMenu.ino ├── Motion │ └── Motion.ino ├── Mutex │ └── Mutex.ino ├── Queue │ └── Queue.ino └── Settings │ ├── Settings.h │ └── Settings.ino ├── library.properties └── src ├── Audio ├── ChirpSystem.cpp ├── ChirpSystem.h ├── Notes.hpp ├── Piezo.cpp ├── Piezo.h ├── Piezo.impl ├── PiezoDAC.impl └── PiezoPWM.impl ├── Bitmaps └── Bitmaps.hpp ├── Buffer ├── DataBuffer.cpp ├── DataBuffer.h ├── FSBuffer.cpp ├── FSBuffer.h ├── LazyDataBuffer.cpp ├── LazyDataBuffer.h ├── RingBuffer.cpp └── RingBuffer.h ├── CircuitOS.h ├── Devices ├── AW9523.cpp ├── AW9523.h ├── Matrix │ ├── DelayedMatrixOutput.cpp │ ├── DelayedMatrixOutput.h │ ├── Font.hpp │ ├── IS31FL3731.cpp │ ├── IS31FL3731.h │ ├── Matrix.cpp │ ├── Matrix.h │ ├── MatrixAnim.cpp │ ├── MatrixAnim.h │ ├── MatrixAnimGIF.cpp │ ├── MatrixAnimGIF.h │ ├── MatrixOutput.cpp │ ├── MatrixOutput.h │ ├── MatrixOutputBuffer.cpp │ ├── MatrixOutputBuffer.h │ ├── MatrixPartOutput.cpp │ ├── MatrixPartOutput.h │ ├── MatrixPixel.cpp │ └── MatrixPixel.h ├── Motion │ ├── ICM20948.cpp │ ├── ICM20948.h │ ├── ICM20948.impl │ ├── MPU6050_CM.cpp │ ├── MPU6050_CM.h │ ├── MPU6050_CM.impl │ ├── img_dmp3a_icm20948.hpp │ └── img_dmp_mpu6050.hpp ├── SerialFlash │ ├── SerialFlashFileAdapter.cpp │ ├── SerialFlashFileAdapter.h │ └── SerialFlashFileAdapter.impl ├── ShiftOutput.cpp └── ShiftOutput.h ├── Display ├── AnimatedSprite.cpp ├── AnimatedSprite.h ├── Color.cpp ├── Color.h ├── Display.cpp ├── Display.h ├── GIFAnimatedSprite.cpp ├── GIFAnimatedSprite.h ├── LovyanGFX_setup.h ├── Sprite.cpp └── Sprite.h ├── Elements ├── GridMenu.cpp ├── GridMenu.h ├── ListMenu.cpp ├── ListMenu.h ├── ModalBackground.hpp ├── SliderElement.cpp └── SliderElement.h ├── FS ├── CompressedFile.cpp ├── CompressedFile.h ├── PGMFile.cpp ├── PGMFile.h ├── RamFile.cpp └── RamFile.h ├── Input ├── I2cExpander.cpp ├── I2cExpander.h ├── Input.cpp ├── Input.h ├── InputGPIO.cpp ├── InputGPIO.h ├── InputI2C.cpp ├── InputI2C.h ├── InputListener.cpp ├── InputListener.h ├── InputShift.cpp └── InputShift.h ├── Loop ├── LoopListener.cpp ├── LoopListener.h ├── LoopManager.cpp └── LoopManager.h ├── Motion ├── Fusion.hpp ├── MPU.cpp ├── MPU.h ├── MPUFusion.cpp ├── MPUFusion.h └── vec.hpp ├── Network ├── Net.cpp ├── Net.h ├── Net.impl ├── StreamableHTTPClient.cpp ├── StreamableHTTPClient.h └── StreamableHTTPClient.impl ├── Support ├── Context.cpp ├── Context.h ├── ContextTransition.cpp ├── ContextTransition.h ├── LowRamContextTransition.impl ├── Modal.cpp ├── Modal.h ├── ModalTransition.cpp ├── ModalTransition.h └── StandardContextTransition.impl ├── Sync ├── BinarySemaphone.cpp ├── BinarySemaphore.h ├── BinarySemaphore.impl ├── Mutex.cpp ├── Mutex.h ├── Mutex.impl ├── Queue.cpp ├── Queue.h ├── Queue.impl ├── Semaphone.cpp ├── Semaphore.h └── Semaphore.impl ├── UI ├── BitmapElement.cpp ├── BitmapElement.h ├── CacheLayout.cpp ├── CacheLayout.h ├── CustomElement.cpp ├── CustomElement.h ├── Element.cpp ├── Element.h ├── ElementContainer.cpp ├── ElementContainer.h ├── GridLayout.cpp ├── GridLayout.h ├── Image.cpp ├── Image.h ├── Layout.cpp ├── Layout.h ├── LinearLayout.cpp ├── LinearLayout.h ├── LowRamScreen.impl ├── Screen.cpp ├── Screen.h ├── ScrollLayout.cpp ├── ScrollLayout.h ├── SpriteElement.cpp ├── SpriteElement.h ├── StandardScreen.impl ├── TextElement.cpp └── TextElement.h └── Util ├── Debug.cpp ├── Debug.h ├── GIF.cpp ├── GIF.h ├── HWRevision.cpp ├── HWRevision.h ├── NullStream.cpp ├── NullStream.h ├── PinMap.h ├── Settings.cpp ├── Settings.h ├── SettingsLittleFS.impl ├── SettingsNVS.impl ├── Task.cpp ├── Task.h ├── Task.impl ├── Timer.cpp ├── Timer.h ├── Vector.h ├── WithListeners.h ├── gifdec.cpp └── gifdec.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-debug 3 | build 4 | desktop.ini -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | stages { 5 | stage("Link") { 6 | steps { 7 | echo 'Linking...' 8 | sh 'echo $(pwd)' 9 | sh 'rm ~/Arduino/libraries/${PWD##*/}' 10 | sh 'ln -s $(pwd) ~/Arduino/libraries/${PWD##*/}' 11 | } 12 | } 13 | 14 | stage('Build') { 15 | steps { 16 | echo 'Building...' 17 | sh 'mkdir -p CircuitOS' 18 | sh 'cp CircuitOS.ino CircuitOS/' 19 | sh 'cd CircuitOS && ~/.arduino/arduino-cli compile -b cm:esp32:ringo -n CircuitOS.ino' 20 | } 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 CircuitMess 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | name = Settings 2 | dir = examples/Settings 3 | port = /dev/ttyUSB0 4 | 5 | ACLI = /home/cm/.arduino/arduino-cli 6 | 7 | srcFiles = $(shell find $(dir)/src/ -name '*.cpp') $(shell find src/ -name '*.cpp') 8 | headerFiles = $(shell find $(dir)/src/ -name '*.h') 9 | 10 | .PHONY: upload clean 11 | 12 | build: $(dir)/build/$(name).bin 13 | 14 | $(dir)/build/$(name).bin: $(srcFiles) $(headerFiles) $(dir)/$(name).ino 15 | @mkdir -p $(dir)/build 16 | cd $(dir); \ 17 | $(ACLI) compile --fqbn cm:esp32:ringo --optimize-for-debug -o build/$(name).bin $(name).ino 18 | 19 | upload: $(dir)/build/$(name).bin 20 | @cd $(dir); \ 21 | $(ACLI) upload --fqbn cm:esp32:ringo -p $(port) -i build/$(name).bin 22 | 23 | clean: 24 | cd $(dir); \ 25 | rm -rf build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircuitOS 2 | Core OS and library used in every firmware for CircuitMess' boards. 3 | 4 | Contains everything from audio, display and UI, button input to task syncing abstractions. 5 | -------------------------------------------------------------------------------- /Setup.hpp: -------------------------------------------------------------------------------- 1 | 2 | /** ### Features ### */ 3 | // #define CIRCUITOS_FREERTOS // Uncomment this line if you're compiling for a core with FreeRTOS 4 | 5 | /** Task and synchronization classes are only available if we're compiling with FreeRTOS */ 6 | #ifdef CIRCUITOS_FREERTOS 7 | #define CIRCUITOS_TASK 8 | #define CIRCUITOS_MUTEX 9 | #define CIRCUITOS_BINARY_SEMAPHORE 10 | #define CIRCUITOS_SEMAPHORE 11 | #define CIRCUITOS_QUEUE 12 | #endif 13 | 14 | /** Use the LovyanGFX library (https://github.com/lovyan03/LovyanGFX) instead of TFT_eSPI 15 | * The library has better performance on higher-res screens (320x240 and above). */ 16 | // #define CIRCUITOS_LOVYANGFX 17 | 18 | /** Zoom factor for the display buffer, 2 means every pixel in the buffer equates to 4 pixels on-screen. */ 19 | // #define CIRCUITOS_LOVYANGFX_ZOOM 1 20 | 21 | /** Whether NVS (non-volatile storage) is present in the core. Enables NVS variant of Settings */ 22 | // #define CIRCUITOS_NVS 23 | 24 | /** Whether LittleFS library is available. Enables LittleFS variant of Settings */ 25 | // #define CIRCUITOS_LITTLEFS 26 | 27 | /** Whether global `tone` and `noTone` functions are available. Required for Audio/Piezo */ 28 | // #define CIRCUITOS_TONE 29 | 30 | /** Enable Piezo using DAC */ 31 | // #define CIRCUITOS_PIEZO_DAC 32 | #define CIRCUITOS_PIEZO_DAC_TIMER 0 // Timer # to use 33 | #define CIRCUITOS_PIEZO_DAC_SD 13 // DAC shutdown IO pin 34 | 35 | /** Enable Piezo for using PWM */ 36 | // #define CIRCUITOS_PIEZO_PWM 37 | #define CIRCUITOS_PIEZO_PWM_CHANNEL 0 38 | 39 | /** Context transitions for devices with little RAM won't keep both context sprites in RAM at the same time. */ 40 | // #define CIRCUITOS_LOWRAM 41 | 42 | /** ### Device drivers ### */ 43 | // #define CIRCUITOS_ICM20948 44 | // #define CIRCUITOS_MPU6050 45 | // #define CIRCUITOS_LEDMATRIX 46 | // #define CIRCUITOS_SERIALFLASH 47 | 48 | /** Using u8g2 fonts uses up some RAM and PROGMEM for every font used. 49 | * The U8g2_for_TFT_eSPI library is used. (https://github.com/Bodmer/U8g2_for_TFT_eSPI)*/ 50 | // #define CIRCUITOS_U8G2FONTS 51 | 52 | /** Using WiFi **/ 53 | // #define CIRCUITOS_NET 54 | -------------------------------------------------------------------------------- /examples/ListMenu/ListMenu.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define BTN_A 32 16 | #define BTN_B 34 17 | #define BTN_C 39 18 | #define BTN_D 36 19 | 20 | Display display(128, 128, 18, 4); 21 | Screen screen(display); 22 | ListMenu menu(&screen, "ListMenu"); 23 | 24 | #define ELEMENTS 8 25 | 26 | Input* input; 27 | 28 | void setUI(); 29 | void scroll(); 30 | void selectElement(uint element); 31 | 32 | void btnRPress(){ 33 | menu.selectNext(); 34 | screen.commit(); 35 | } 36 | 37 | void btnLPress(){ 38 | menu.selectPrev(); 39 | screen.commit(); 40 | } 41 | 42 | 43 | void setup(){ 44 | Serial.begin(115200); 45 | input = new InputGPIO(); 46 | 47 | setUI(); 48 | screen.draw(); 49 | screen.commit(); 50 | 51 | input->setBtnReleaseCallback(BTN_A, btnRPress); 52 | input->setBtnPressCallback(BTN_B, btnLPress); 53 | 54 | LoopManager::addListener(input); 55 | } 56 | 57 | void loop(){ 58 | LoopManager::loop(); 59 | } 60 | 61 | void setUI(){ 62 | display.clear(TFT_GREEN); 63 | 64 | menu.setWHType(PARENT, PARENT); 65 | for(int i = 0; i < ELEMENTS; i++){ 66 | menu.addItem("Foo "); 67 | } 68 | menu.reflow(); 69 | 70 | screen.addChild(&menu); 71 | screen.repos(); 72 | } -------------------------------------------------------------------------------- /examples/Motion/Motion.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define BTN_A 32 12 | #define BTN_B 34 13 | #define BTN_C 39 14 | #define BTN_D 36 15 | 16 | Display* display = new Display(128, 128, 18, 2); 17 | Sprite* sprite = display->getBaseSprite(); 18 | 19 | MPU* mpu; 20 | vec3f euler = { 0, 0, 0 }; 21 | vec3f accel = { 0, 0, 0 }; 22 | vec3f vel = { 0, 0, 0 }; 23 | 24 | void btnA(){ 25 | 26 | } 27 | 28 | void printDisplay(){ 29 | vel = { 0, 0, 0 }; 30 | mpu->readSensor(); 31 | vec3f _euler = mpu->getEuler(); 32 | vec3f _accel = mpu->getAccel(); 33 | 34 | 35 | sprite->clear(TFT_BLACK); 36 | sprite->setCursor(0, 0); 37 | 38 | sprite->println("\n Gyro"); 39 | sprite->printf(" Pitch: %c%.2f rad\n", _euler.pitch < 0 ? '-' : ' ', fabs(_euler.pitch)); 40 | sprite->printf(" Yaw: %c%.2f rad\n", _euler.yaw < 0 ? '-' : ' ', fabs(_euler.yaw)); 41 | sprite->printf(" Roll: %c%.2f rad\n", _euler.roll < 0 ? '-' : ' ', fabs(_euler.roll)); 42 | 43 | sprite->println("\n Accel"); 44 | sprite->printf(" X: %c%.2f m/s/s\n", _accel.x < 0 ? '-' : ' ', fabs(_accel.x)); 45 | sprite->printf(" Y: %c%.2f m/s/s\n", _accel.y < 0 ? '-' : ' ', fabs(_accel.y)); 46 | sprite->printf(" Z: %c%.2f m/s/s\n", _accel.z < 0 ? '-' : ' ', fabs(_accel.z)); 47 | 48 | sprite->println("\n Velocity"); 49 | sprite->printf(" X: %c%.2f m/s\n", vel.x < 0 ? '-' : ' ', fabs(vel.x * 2000)); 50 | sprite->printf(" Y: %c%.2f m/s\n", vel.y < 0 ? '-' : ' ', fabs(vel.y * 2000)); 51 | sprite->printf(" Z: %c%.2f m/s\n", vel.z < 0 ? '-' : ' ', fabs(vel.z * 2000)); 52 | 53 | display->commit(); 54 | 55 | delay(25); 56 | } 57 | 58 | void displayTask(Task* task){ 59 | while(task->running){ 60 | printDisplay(); 61 | delay(20); 62 | } 63 | } 64 | 65 | void setup(){ 66 | Serial.begin(115200); 67 | 68 | /*new InputGPIO(); 69 | Input::getInstance()->setBtnPressCallback(BTN_A, btnA); 70 | Input::getInstance()->start();*/ 71 | 72 | sprite->clear(TFT_BLACK); 73 | sprite->setTextFont(1); 74 | sprite->setTextSize(1); 75 | sprite->setTextColor(TFT_WHITE); 76 | sprite->println("\n Calibrating...\n"); 77 | display->commit(); 78 | 79 | mpu = new MPU6050_CM(22, 21); 80 | mpu->begin(); 81 | //mpu->calibrate(); 82 | delay(100); 83 | 84 | Task dTask("DisplayTask", displayTask); 85 | dTask.start(2); 86 | //LoopManager::startTask(); 87 | } 88 | 89 | void loop(){ 90 | delay(500); 91 | //printDisplay(); 92 | //LoopManager::loop(); 93 | /*mpu->readSensor(); 94 | euler = mpu->getEuler(); 95 | accel = mpu->getAccel(); 96 | Serial.printf("Euler/Accel: [ %.2f %.2f %.2f ], [ %.2f %.2f %.2f ]\n", euler.pitch, euler.yaw, euler.roll, accel.x, accel.y, accel.z);*/ 97 | 98 | /*float pitch = atan2(-accel.x, -accel.z); 99 | float roll = -atan(-accel.y / sqrt(accel.x * accel.x + accel.z * accel.z)); 100 | euler = { pitch, mpu->getEuler().yaw, roll };*/ 101 | //euler = mpu->getEuler(); 102 | 103 | /*vec3f grav = { 0, 0, -1 }; 104 | quat rot(euler); 105 | grav = rot.rotate(grav); 106 | grav.y *= -1; 107 | grav *= -1; 108 | accel += grav;*/ 109 | 110 | //Serial.printf("Euler/Accel: [ %.2f %.2f %.2f ], [ %.2f %.2f %.2f ]\n", euler.pitch, euler.yaw, euler.roll, accel.x, accel.y, accel.z); 111 | //delay(20); 112 | //printDisplay(); 113 | } -------------------------------------------------------------------------------- /examples/Mutex/Mutex.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void loop_task(Task* task); 8 | 9 | Mutex mutex; 10 | Task task("OtherTask", loop_task); 11 | 12 | void setup(){ 13 | Serial.begin(115200); 14 | task.start(); 15 | } 16 | 17 | void loop(){ 18 | Serial.println("Task 1 trying to enter critical section"); 19 | 20 | mutex.lock(); // -- critical section begins 21 | 22 | Serial.println("Task 1 entered"); 23 | usleep(1000 * (2000 + rand() % 1000)); 24 | Serial.println("Task 1 exiting critical section"); 25 | 26 | mutex.unlock(); // - critical section end 27 | 28 | usleep(1000 * (2000 + rand() % 1000)); 29 | } 30 | 31 | void loop_task(Task* task){ 32 | while(task->running){ 33 | Serial.println("Task 2 trying to enter critical section"); 34 | 35 | mutex.lock(); // -- critical section begins 36 | 37 | Serial.println("Task 2 entered"); 38 | usleep(1000 * (2000 + rand() % 1000)); 39 | Serial.println("Task 2 exiting critical section"); 40 | 41 | mutex.unlock(); // - critical section end 42 | 43 | usleep(1000 * (2000 + rand() % 1000)); 44 | } 45 | } -------------------------------------------------------------------------------- /examples/Queue/Queue.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void loop_task(Task* task); 7 | Task task("OtherTask", loop_task); 8 | 9 | Queue queue(10, sizeof(byte)); 10 | byte message = 0x01; 11 | 12 | void setup(){ 13 | Serial.begin(115200); 14 | Serial.println(); 15 | 16 | Serial.println("Posting 3 messages to queue"); 17 | queue.send(&message); 18 | queue.send(&message); 19 | queue.send(&message); 20 | 21 | task.start(); 22 | } 23 | 24 | void loop(){ 25 | delay(2000 + rand() % 1000); 26 | 27 | Serial.println("-> Posting message to queue"); 28 | delay(5); 29 | queue.send(&message); 30 | } 31 | 32 | void loop_task(Task* task){ 33 | while(task->running){ 34 | Serial.println("-- Reading message from queue"); 35 | delay(5); 36 | 37 | queue.receive(&message); 38 | 39 | Serial.printf("<- Message read, %d left\n", queue.count()); 40 | 41 | delay(200); 42 | } 43 | } -------------------------------------------------------------------------------- /examples/Settings/Settings.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct SettingsStruct { 4 | int foo; 5 | bool bar; 6 | long long unsigned chungus; 7 | }; 8 | 9 | SettingsStruct* settings(){ 10 | return static_cast(Settings::data()); 11 | } -------------------------------------------------------------------------------- /examples/Settings/Settings.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Settings.h" 4 | 5 | void setup(){ 6 | Serial.begin(115200); 7 | Serial.println(); 8 | 9 | bool exists = Settings::init(new SettingsStruct, sizeof(SettingsStruct)); 10 | Serial.printf("Settings%s exist\n", exists ? "" : " don't"); 11 | 12 | if(exists){ 13 | Serial.printf("A: %d, B: %s, C: %llu\n", settings()->foo, settings()->bar ? "yay" : "nay", settings()->chungus); 14 | }else{ 15 | settings()->foo = settings()->bar = settings()->chungus = 0; 16 | } 17 | 18 | settings()->foo++; 19 | Settings::store(); 20 | 21 | Serial.printf("Boots: %d\n", settings()->foo); 22 | } 23 | 24 | void loop(){ 25 | 26 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=CircuitOS 2 | version=1.7.1 3 | author=CircuitMess 4 | maintainer=Filip Budiša 5 | sentence=Circuit OS is our custom-made operating system built on top of FreeRTOS. 6 | paragraph=See more on https://www.circuitmess.com/circuitos/ 7 | category=Device Control 8 | url=https://github.com/CircuitMess/CircuitOS 9 | architectures=esp32,esp8266 10 | includes=CircuitOS.h 11 | -------------------------------------------------------------------------------- /src/Audio/ChirpSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "ChirpSystem.h" 2 | #include "Piezo.h" 3 | 4 | ChirpSystem::ChirpSystem() : task("ChirpSystem", [](Task* task){ static_cast(task->arg)->playbackFunc(); }, 2048, this){ 5 | 6 | } 7 | 8 | void ChirpSystem::play(std::initializer_list sound){ 9 | if(!task.running){ 10 | current = sound; 11 | startMillis = millis(); 12 | task.start(1, 0); 13 | }else{ 14 | mut.lock(); 15 | queued = sound; 16 | mut.unlock(); 17 | } 18 | } 19 | 20 | void ChirpSystem::play(const Sound& sound){ 21 | if(!task.running){ 22 | current = sound; 23 | startMillis = millis(); 24 | task.start(1, 0); 25 | }else{ 26 | mut.lock(); 27 | queued = sound; 28 | mut.unlock(); 29 | } 30 | } 31 | 32 | void ChirpSystem::stop(){ 33 | task.stop(true); 34 | queued.clear(); 35 | Piezo.noTone(); 36 | } 37 | 38 | void ChirpSystem::playbackFunc(){ 39 | while(chirpID < current.size()){ 40 | mut.lock(); 41 | if(!queued.empty()){ 42 | current = queued; 43 | queued.clear(); 44 | chirpID = 0; 45 | startMillis = millis(); 46 | mut.unlock(); 47 | continue; 48 | } 49 | mut.unlock(); 50 | 51 | currentMillis = millis() - startMillis; 52 | 53 | uint16_t freq = map(currentMillis, 0, current[chirpID].duration, current[chirpID].startFreq, current[chirpID].endFreq); 54 | Piezo.tone(freq, 0); //duration of 0 equates to a continuous sound 55 | 56 | vTaskDelay(pdMS_TO_TICKS(3 + (int)(1000.0 / (double)freq))); 57 | 58 | if(current[chirpID].duration > currentMillis) continue; 59 | 60 | startMillis = millis(); 61 | chirpID++; 62 | 63 | if(chirpID >= current.size()){ 64 | mut.lock(); 65 | current = queued; 66 | queued.clear(); 67 | mut.unlock(); 68 | chirpID = 0; 69 | } 70 | } 71 | Piezo.noTone(); 72 | } -------------------------------------------------------------------------------- /src/Audio/ChirpSystem.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITMESS_AUDIO_AUDIOSYSTEM_H 2 | #define CIRCUITMESS_AUDIO_AUDIOSYSTEM_H 3 | 4 | #include "../Util/Task.h" 5 | #include "../Sync/Mutex.h" 6 | 7 | /** 8 | * A chirp is a waveform that “sweeps” from a starting frequency to an ending frequency, during the specified duration of time. 9 | */ 10 | struct Chirp{ 11 | uint16_t startFreq; //[Hz] 12 | uint16_t endFreq; //[Hz] 13 | uint32_t duration; //[ms] 14 | }; 15 | 16 | typedef std::vector Sound; 17 | 18 | /** 19 | * Simple Audio system designed for short SFX played on a simple piezo buzzer. 20 | */ 21 | class ChirpSystem { 22 | public: 23 | ChirpSystem(); 24 | 25 | /** 26 | * Plays the specified sound, interrupts the currently playing sound. 27 | */ 28 | void play(std::initializer_list sound); 29 | void play(const Sound& sound); 30 | 31 | void stop(); 32 | 33 | private: 34 | Task task; 35 | Sound queued; 36 | Sound current; 37 | 38 | Mutex mut; 39 | 40 | void playbackFunc(); 41 | volatile uint32_t startMillis = 0; 42 | volatile uint32_t currentMillis = 0; 43 | volatile uint32_t chirpID = 0; 44 | }; 45 | 46 | #endif //CIRCUITMESS_AUDIO_AUDIOSYSTEM_H 47 | -------------------------------------------------------------------------------- /src/Audio/Notes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_NOTES_HPP 2 | #define CIRCUITOS_NOTES_HPP 3 | 4 | #define NOTE_B0 31 5 | #define NOTE_C1 33 6 | #define NOTE_CS1 35 7 | #define NOTE_D1 37 8 | #define NOTE_DS1 39 9 | #define NOTE_E1 41 10 | #define NOTE_F1 44 11 | #define NOTE_FS1 46 12 | #define NOTE_G1 49 13 | #define NOTE_GS1 52 14 | #define NOTE_A1 55 15 | #define NOTE_AS1 58 16 | #define NOTE_B1 62 17 | #define NOTE_C2 65 18 | #define NOTE_CS2 69 19 | #define NOTE_D2 73 20 | #define NOTE_DS2 78 21 | #define NOTE_E2 82 22 | #define NOTE_F2 87 23 | #define NOTE_FS2 93 24 | #define NOTE_G2 98 25 | #define NOTE_GS2 104 26 | #define NOTE_A2 110 27 | #define NOTE_AS2 117 28 | #define NOTE_B2 123 29 | #define NOTE_C3 131 30 | #define NOTE_CS3 139 31 | #define NOTE_D3 147 32 | #define NOTE_DS3 156 33 | #define NOTE_E3 165 34 | #define NOTE_F3 175 35 | #define NOTE_FS3 185 36 | #define NOTE_G3 196 37 | #define NOTE_GS3 208 38 | #define NOTE_A3 220 39 | #define NOTE_AS3 233 40 | #define NOTE_B3 247 41 | #define NOTE_C4 262 42 | #define NOTE_CS4 277 43 | #define NOTE_D4 294 44 | #define NOTE_DS4 311 45 | #define NOTE_E4 330 46 | #define NOTE_F4 349 47 | #define NOTE_FS4 370 48 | #define NOTE_G4 392 49 | #define NOTE_GS4 415 50 | #define NOTE_A4 440 51 | #define NOTE_AS4 466 52 | #define NOTE_B4 494 53 | #define NOTE_C5 523 54 | #define NOTE_CS5 554 55 | #define NOTE_D5 587 56 | #define NOTE_DS5 622 57 | #define NOTE_E5 659 58 | #define NOTE_F5 698 59 | #define NOTE_FS5 740 60 | #define NOTE_G5 784 61 | #define NOTE_GS5 831 62 | #define NOTE_A5 880 63 | #define NOTE_AS5 932 64 | #define NOTE_B5 988 65 | #define NOTE_C6 1047 66 | #define NOTE_CS6 1109 67 | #define NOTE_D6 1175 68 | #define NOTE_DS6 1245 69 | #define NOTE_E6 1319 70 | #define NOTE_F6 1397 71 | #define NOTE_FS6 1480 72 | #define NOTE_G6 1568 73 | #define NOTE_GS6 1661 74 | #define NOTE_A6 1760 75 | #define NOTE_AS6 1865 76 | #define NOTE_B6 1976 77 | #define NOTE_C7 2093 78 | #define NOTE_CS7 2217 79 | #define NOTE_D7 2349 80 | #define NOTE_DS7 2489 81 | #define NOTE_E7 2637 82 | #define NOTE_F7 2794 83 | #define NOTE_FS7 2960 84 | #define NOTE_G7 3136 85 | #define NOTE_GS7 3322 86 | #define NOTE_A7 3520 87 | #define NOTE_AS7 3729 88 | #define NOTE_B7 3951 89 | #define NOTE_C8 4186 90 | #define NOTE_CS8 4435 91 | #define NOTE_D8 4699 92 | #define NOTE_DS8 4978 93 | 94 | #endif //CIRCUITOS_NOTES_HPP 95 | -------------------------------------------------------------------------------- /src/Audio/Piezo.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | #include "Notes.hpp" 3 | #ifdef CIRCUITOS_TONE 4 | #include "Piezo.impl" 5 | 6 | #elif defined CIRCUITOS_PIEZO_PWM 7 | #include "PiezoPWM.impl" 8 | 9 | #elif defined CIRCUITOS_PIEZO_DAC 10 | #include "PiezoDAC.impl" 11 | #endif -------------------------------------------------------------------------------- /src/Audio/Piezo.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_PIEZO_H 2 | #define CIRCUITOS_PIEZO_H 3 | 4 | #include 5 | 6 | #ifdef CIRCUITOS_PIEZO_PWM 7 | #include 8 | 9 | #elif defined CIRCUITOS_PIEZO_DAC 10 | #include 11 | #include "soc/sens_reg.h" 12 | #endif 13 | 14 | class PiezoImpl { 15 | public: 16 | void begin(uint8_t pin); 17 | uint8_t getVolume() const { return volume;} 18 | void setVolume(uint8_t volume) { this->volume = volume;} 19 | void setMute(bool mute); 20 | bool isMuted() const; 21 | 22 | void tone(uint16_t freq, uint16_t duration = 0); 23 | void noTone(); 24 | 25 | #ifdef CIRCUITOS_PIEZO_PWM 26 | static void interrupt(void* arg); 27 | timer_isr_handle_t timer; 28 | #elif defined CIRCUITOS_PIEZO_DAC 29 | static void IRAM_ATTR timerInterrupt(); 30 | #endif 31 | 32 | private: 33 | volatile uint8_t pin = -1; 34 | bool mute = false; 35 | volatile uint8_t volume = 5; 36 | 37 | #ifdef CIRCUITOS_PIEZO_DAC 38 | hw_timer_t* timer = nullptr; 39 | const uint16_t ampBootTime = 50; // How long it takes amp to power on [ms] 40 | const uint16_t powerHold = 20; // How long to keep amp powered on after last tone [ms] 41 | volatile enum { OFF, POWERING, ON, IDLE } state = OFF; 42 | volatile uint32_t currentFreq = 0; 43 | volatile uint32_t currentDuration = 0; 44 | volatile uint32_t counter = 0; 45 | #endif 46 | 47 | }; 48 | 49 | extern PiezoImpl Piezo; 50 | 51 | #endif //CIRCUITOS_PIEZO_H 52 | -------------------------------------------------------------------------------- /src/Audio/Piezo.impl: -------------------------------------------------------------------------------- 1 | #include "Piezo.h" 2 | 3 | PiezoImpl Piezo; 4 | 5 | void PiezoImpl::begin(uint8_t pin){ 6 | this->pin = pin; 7 | } 8 | 9 | void PiezoImpl::tone(uint16_t freq, uint16_t duration){ 10 | if(pin == (uint8_t) -1) return; 11 | if(mute) return; 12 | if(volume == 0) return; 13 | ::tone(pin, freq, duration); 14 | } 15 | 16 | void PiezoImpl::noTone(){ 17 | if(pin == (uint8_t) -1) return; 18 | ::noTone(pin); 19 | } 20 | 21 | void PiezoImpl::setMute(bool mute){ 22 | this->mute = mute; 23 | 24 | if(mute){ 25 | this->noTone(); 26 | } 27 | } 28 | 29 | bool PiezoImpl::isMuted() const{ 30 | return mute; 31 | } 32 | -------------------------------------------------------------------------------- /src/Audio/PiezoDAC.impl: -------------------------------------------------------------------------------- 1 | #include "Piezo.h" 2 | #include 3 | 4 | #include "../Util/Timer.h" 5 | 6 | PiezoImpl Piezo; 7 | 8 | #ifdef CIRCUITOS_PIEZO_DAC_TIMER 9 | #define TIMER CIRCUITOS_PIEZO_DAC_TIMER 10 | #else 11 | #define TIMER 0 12 | #endif 13 | 14 | #ifdef CIRCUITOS_PIEZO_DAC_SD 15 | #define DAC_SD CIRCUITOS_PIEZO_DAC_SD 16 | #else 17 | #define DAC_SD 13 18 | #endif 19 | 20 | 21 | void PiezoImpl::begin(uint8_t pin){ 22 | this->pin = pin; 23 | 24 | state = OFF; 25 | pinMode(DAC_SD, OUTPUT); 26 | digitalWrite(DAC_SD, HIGH); 27 | dacWrite(pin, 127); 28 | 29 | // Set up interrupt routine 30 | // 20 kHz, 50 uS 31 | 32 | if(timer != nullptr){ 33 | timerEnd(timer); 34 | timer = nullptr; 35 | } 36 | 37 | timer = timerBegin(TIMER, 80, true); 38 | timerAttachInterrupt(timer, &timerInterrupt, true); // P3= edge triggered 39 | dis(timer); 40 | 41 | noTone(); 42 | } 43 | 44 | void PiezoImpl::tone(uint16_t freq, uint16_t duration){ 45 | if(pin == (uint8_t) -1) return; 46 | if(mute) return; 47 | if(volume == 0) return; 48 | 49 | currentFreq = freq; 50 | currentDuration = duration; 51 | counter = 0; 52 | 53 | if(state == OFF){ 54 | dis(timer); 55 | dacWrite(pin, 127); 56 | 57 | state = POWERING; 58 | digitalWrite(DAC_SD, LOW); 59 | 60 | timerAlarmWrite(timer, ampBootTime * 1000, true); 61 | timerAlarmEnable(timer); 62 | timerStart(timer); 63 | }else if(state == IDLE){ 64 | state = ON; 65 | timerAlarmWrite(timer, 50, true); 66 | timerWrite(timer, 0); 67 | } 68 | } 69 | 70 | void PiezoImpl::noTone(){ 71 | if(pin == (uint8_t) -1) return; 72 | if(state == IDLE) return; 73 | 74 | dis(timer); 75 | dacWrite(pin, 127); 76 | 77 | if(state == ON || state == POWERING){ 78 | CM::timerAlarmWrite(timer, powerHold * 1000, true); 79 | timerAlarmEnable(timer); 80 | timerStart(timer); 81 | state = IDLE; 82 | } 83 | } 84 | 85 | void PiezoImpl::setMute(bool mute){ 86 | this->mute = mute; 87 | 88 | if(mute){ 89 | this->noTone(); 90 | } 91 | } 92 | 93 | bool PiezoImpl::isMuted() const{ 94 | return mute; 95 | } 96 | 97 | void IRAM_ATTR PiezoImpl::timerInterrupt(){ 98 | PiezoImpl* piezo = &Piezo; 99 | 100 | if(piezo->state == OFF || piezo->mute || piezo->volume == 0){ 101 | dis(piezo->timer); 102 | //ets_printf("OUT\n"); 103 | return; 104 | }else if(piezo->state == POWERING){ 105 | CM::timerAlarmWrite(piezo->timer, 50, true); 106 | piezo->state = ON; 107 | //ets_printf("POWERED\n"); 108 | }else if(piezo->state == IDLE){ 109 | dis(piezo->timer); 110 | piezo->state = OFF; 111 | digitalWrite(DAC_SD, HIGH); 112 | //ets_printf("OFF\n"); 113 | return; 114 | } 115 | 116 | double value = sin((double) piezo->currentFreq * 2.0 * M_PI * ((double) piezo->counter / 20000.0)); // wave [-1, 1] 117 | // value /= 3.0; // counter voltage amplification 118 | // value /= 3.0; // make sure we don't draw too much current 119 | value *= (double) piezo->volume / 255.0; // apply volume [-1, 1] 120 | value = value / 2.0 + 0.5; // bring to [0, 1] 121 | value *= 255.0; // bring to [0, 255] 122 | value = value > 255 ? 255 : value; // clipping 123 | value = value < 0 ? 0 : value; // clipping 124 | 125 | dacWrite(piezo->pin, (uint8_t) value); 126 | //ets_printf("%d\n", (uint8_t) value); 127 | 128 | piezo->counter++; 129 | 130 | if(piezo->currentDuration != 0 && piezo->counter * 50 >= piezo->currentDuration * 1000){ 131 | CM::timerAlarmWrite(piezo->timer, piezo->powerHold * 1000, true); 132 | piezo->state = IDLE; 133 | 134 | dacWrite(piezo->pin, 127); 135 | piezo->currentFreq = 0; 136 | piezo->currentDuration = 0; 137 | piezo->counter = 0; 138 | 139 | //ets_printf("DONE\n"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Audio/PiezoPWM.impl: -------------------------------------------------------------------------------- 1 | #include "Piezo.h" 2 | #include 3 | 4 | PiezoImpl Piezo; 5 | 6 | #ifndef CIRCUITOS_PIEZO_PWM_CHANNEL 7 | #define CIRCUITOS_PIEZO_PWM_CHANNEL 0 8 | #endif 9 | 10 | #define CHANNEL CIRCUITOS_PIEZO_PWM_CHANNEL 11 | 12 | 13 | void PiezoImpl::begin(uint8_t pin){ 14 | this->pin = pin; 15 | 16 | rtc_cpu_freq_config_t freqConfig; 17 | rtc_clk_cpu_freq_get_config(&freqConfig); 18 | 19 | timer_config_t config = { 20 | .alarm_en = false, 21 | .counter_en = false, 22 | .intr_type = TIMER_INTR_LEVEL, 23 | .counter_dir = TIMER_COUNT_DOWN, 24 | .auto_reload = false, 25 | .divider = freqConfig.freq_mhz 26 | }; 27 | timer_init(TIMER_GROUP_1, TIMER_0, &config); 28 | timer_set_alarm_value(TIMER_GROUP_1, TIMER_0, 0); 29 | timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_EN); 30 | timer_enable_intr(TIMER_GROUP_1, TIMER_0); 31 | timer_isr_register(TIMER_GROUP_1, TIMER_0, PiezoImpl::interrupt, this, 0, &timer); 32 | 33 | ledcAttachPin(pin, CHANNEL); 34 | } 35 | 36 | void PiezoImpl::tone(uint16_t freq, uint16_t duration){ 37 | if(pin == (uint8_t) -1) return; 38 | if(mute) return; 39 | if(volume == 0) return; 40 | 41 | if(duration > 0){ 42 | uint32_t loadValue = duration * 1000; 43 | timer_set_counter_value(TIMER_GROUP_1, TIMER_0, loadValue); 44 | timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_EN); 45 | timer_start(TIMER_GROUP_1, TIMER_0); 46 | } 47 | 48 | ledcAttachPin(pin, CHANNEL); 49 | ledcWriteTone(CHANNEL, freq); 50 | } 51 | 52 | void PiezoImpl::noTone(){ 53 | if(pin == (uint8_t) -1) return; 54 | ledcDetachPin(pin); 55 | } 56 | 57 | void PiezoImpl::setMute(bool mute){ 58 | this->mute = mute; 59 | 60 | if(mute){ 61 | this->noTone(); 62 | } 63 | } 64 | 65 | bool PiezoImpl::isMuted() const{ 66 | return mute; 67 | } 68 | 69 | void PiezoImpl::interrupt(void *arg){ 70 | TIMERG1.int_clr_timers.t0 = 1; 71 | TIMERG1.hw_timer[0].config.alarm_en = 0; 72 | TIMERG1.hw_timer[0].config.enable = 0; 73 | 74 | PiezoImpl* piezo = static_cast(arg); 75 | ledcDetachPin(piezo->pin); 76 | } 77 | -------------------------------------------------------------------------------- /src/Buffer/DataBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "DataBuffer.h" 2 | 3 | DataBuffer::DataBuffer(const size_t size, bool local) : size(size){ 4 | #ifdef CONFIG_SPIRAM_SUPPORT 5 | if(psramFound() && !local){ 6 | buffer = static_cast(ps_malloc(size)); 7 | }else{ 8 | buffer = static_cast(malloc(size)); 9 | } 10 | #else 11 | buffer = static_cast(malloc(size)); 12 | #endif 13 | } 14 | 15 | DataBuffer::~DataBuffer(){ 16 | free(buffer); 17 | } 18 | 19 | size_t DataBuffer::readAvailable(){ 20 | return writeCursor - readCursor; 21 | } 22 | 23 | bool DataBuffer::readMove(size_t amount){ 24 | if(readCursor + amount > writeCursor) return false; 25 | readCursor += amount; 26 | return true; 27 | } 28 | 29 | uint8_t* DataBuffer::writeData(){ 30 | if(writeCursor == 0) return buffer; 31 | 32 | size_t left = writeCursor - readCursor; 33 | if(left == 0){ 34 | writeCursor = readCursor = 0; 35 | return buffer; 36 | } 37 | 38 | memmove(buffer, buffer + readCursor, left); 39 | readCursor = 0; 40 | writeCursor = left; 41 | 42 | return buffer + writeCursor; 43 | } 44 | 45 | void DataBuffer::clear(){ 46 | readCursor = 0; 47 | writeCursor = 0; 48 | } 49 | 50 | const uint8_t* DataBuffer::readData(){ 51 | return buffer + readCursor; 52 | } 53 | 54 | size_t DataBuffer::writeAvailable(){ 55 | return readCursor + (size - writeCursor); 56 | } 57 | 58 | bool DataBuffer::writeMove(size_t amount){ 59 | if(writeCursor + amount > size) return false; 60 | writeCursor += amount; 61 | return true; 62 | } 63 | -------------------------------------------------------------------------------- /src/Buffer/DataBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef JAYD_LIBRARY_DATABUFFER_H 2 | #define JAYD_LIBRARY_DATABUFFER_H 3 | 4 | #include 5 | 6 | class DataBuffer { 7 | public: 8 | DataBuffer(const size_t size, bool prefferLocal = false); 9 | virtual ~DataBuffer(); 10 | 11 | size_t readAvailable(); 12 | bool readMove(size_t amount); 13 | const uint8_t* readData(); 14 | 15 | size_t writeAvailable(); 16 | bool writeMove(size_t amount); 17 | uint8_t* writeData(); 18 | 19 | void clear(); 20 | 21 | private: 22 | const size_t size; 23 | 24 | uint8_t* buffer = nullptr; 25 | size_t readCursor = 0; 26 | size_t writeCursor = 0; 27 | }; 28 | 29 | 30 | #endif //JAYD_LIBRARY_DATABUFFER_H 31 | -------------------------------------------------------------------------------- /src/Buffer/FSBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "FSBuffer.h" 2 | 3 | FSBuffer::FSBuffer(File file, const size_t size) : file(file), size(size){ 4 | #ifdef CONFIG_SPIRAM_SUPPORT 5 | if(psramFound()){ 6 | buffer = static_cast(ps_malloc(size)); 7 | }else{ 8 | buffer = static_cast(malloc(size)); 9 | } 10 | #else 11 | buffer = static_cast(malloc(size)); 12 | #endif 13 | } 14 | 15 | FSBuffer::~FSBuffer(){ 16 | free(buffer); 17 | } 18 | 19 | size_t FSBuffer::available(){ 20 | return bytesFilled - cursor; 21 | } 22 | 23 | bool FSBuffer::moveRead(size_t amount){ 24 | if(cursor + amount > bytesFilled) return false; 25 | cursor += amount; 26 | return true; 27 | } 28 | 29 | bool FSBuffer::refill(){ 30 | size_t remaining = bytesFilled - cursor; 31 | 32 | if(remaining != 0){ 33 | memmove(buffer, buffer + cursor, remaining); 34 | } 35 | 36 | bytesFilled = file.read(buffer + remaining, size - remaining); 37 | bytesFilled += remaining; 38 | cursor = 0; 39 | 40 | return bytesFilled != 0; 41 | } 42 | 43 | void FSBuffer::clear(){ 44 | cursor = 0; 45 | } 46 | 47 | const uint8_t* FSBuffer::data(){ 48 | return buffer + cursor; 49 | } 50 | 51 | File FSBuffer::getFile() const{ 52 | return file; 53 | } -------------------------------------------------------------------------------- /src/Buffer/FSBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef JAYD_LIBRARY_FSBUFFER_H 2 | #define JAYD_LIBRARY_FSBUFFER_H 3 | 4 | #include 5 | #include 6 | 7 | class FSBuffer { 8 | public: 9 | FSBuffer(File file, const size_t size); 10 | virtual ~FSBuffer(); 11 | 12 | size_t available(); 13 | bool moveRead(size_t amount); 14 | bool refill(); 15 | void clear(); 16 | 17 | const uint8_t* data(); 18 | File getFile() const; 19 | 20 | private: 21 | fs::File file; 22 | const size_t size; 23 | size_t bytesFilled = 0; 24 | 25 | uint8_t* buffer = nullptr; 26 | size_t cursor = 0; 27 | 28 | }; 29 | 30 | 31 | #endif //JAYD_LIBRARY_FSBUFFER_H 32 | -------------------------------------------------------------------------------- /src/Buffer/LazyDataBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "LazyDataBuffer.h" 2 | 3 | LazyDataBuffer::LazyDataBuffer(const size_t size) : size(size){ 4 | #ifdef CONFIG_SPIRAM_SUPPORT 5 | if(psramFound()){ 6 | buffer = static_cast(ps_malloc(size)); 7 | }else{ 8 | buffer = static_cast(malloc(size)); 9 | } 10 | #else 11 | buffer = static_cast(malloc(size)); 12 | #endif 13 | } 14 | 15 | LazyDataBuffer::~LazyDataBuffer(){ 16 | free(buffer); 17 | } 18 | 19 | size_t LazyDataBuffer::readAvailable(){ 20 | return writeCursor - readCursor; 21 | } 22 | 23 | bool LazyDataBuffer::readMove(size_t amount){ 24 | if(readCursor + amount > writeCursor) return false; 25 | readCursor += amount; 26 | return true; 27 | } 28 | 29 | uint8_t* LazyDataBuffer::writeData(){ 30 | return buffer + writeCursor; 31 | } 32 | 33 | void LazyDataBuffer::relocate(){ 34 | size_t left = writeCursor - readCursor; 35 | if(left == 0){ 36 | writeCursor = readCursor = 0; 37 | return; 38 | } 39 | 40 | memmove(buffer, buffer + readCursor, left); 41 | 42 | readCursor = 0; 43 | writeCursor = left; 44 | } 45 | 46 | void LazyDataBuffer::clear(){ 47 | readCursor = 0; 48 | } 49 | 50 | const uint8_t* LazyDataBuffer::readData(){ 51 | return buffer + readCursor; 52 | } 53 | 54 | size_t LazyDataBuffer::writeAvailable(){ 55 | return size - writeCursor; 56 | } 57 | 58 | size_t LazyDataBuffer::potentialWriteAvailable(){ 59 | return readCursor; 60 | } 61 | 62 | bool LazyDataBuffer::writeMove(size_t amount){ 63 | if(writeCursor + amount > size) return false; 64 | writeCursor += amount; 65 | return true; 66 | } 67 | -------------------------------------------------------------------------------- /src/Buffer/LazyDataBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef JAYD_LIBRARY_LAZYDATABUFFER_H 2 | #define JAYD_LIBRARY_LAZYDATABUFFER_H 3 | 4 | #include 5 | 6 | class LazyDataBuffer { 7 | public: 8 | LazyDataBuffer(const size_t size); 9 | virtual ~LazyDataBuffer(); 10 | 11 | size_t readAvailable(); 12 | bool readMove(size_t amount); 13 | const uint8_t* readData(); 14 | 15 | size_t writeAvailable(); 16 | bool writeMove(size_t amount); 17 | uint8_t* writeData(); 18 | 19 | size_t potentialWriteAvailable(); 20 | 21 | void clear(); 22 | void relocate(); 23 | 24 | private: 25 | const size_t size; 26 | 27 | uint8_t* buffer = nullptr; 28 | size_t readCursor = 0; 29 | size_t writeCursor = 0; 30 | }; 31 | 32 | 33 | #endif //JAYD_LIBRARY_LAZYDATABUFFER_H 34 | -------------------------------------------------------------------------------- /src/Buffer/RingBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "RingBuffer.h" 2 | 3 | RingBuffer::RingBuffer(size_t size) : size(size + 1){ 4 | #ifdef CONFIG_SPIRAM_SUPPORT 5 | if(psramFound()){ 6 | buffer = static_cast(ps_malloc(size + 1)); 7 | }else{ 8 | buffer = static_cast(malloc(size + 1)); 9 | } 10 | #else 11 | buffer = static_cast(malloc(size + 1)); 12 | #endif 13 | } 14 | 15 | RingBuffer::~RingBuffer(){ 16 | free(buffer); 17 | } 18 | 19 | size_t RingBuffer::writeAvailable(){ 20 | if(end >= beginning) return size + beginning - end - 1; 21 | else return beginning - end - 1; 22 | } 23 | 24 | size_t RingBuffer::readAvailable(){ 25 | if(end >= beginning) return end - beginning; 26 | else return size + end - beginning; 27 | } 28 | 29 | size_t RingBuffer::read(uint8_t* destination, size_t n){ 30 | n = min(n, readAvailable()); 31 | if(n == 0) return 0; 32 | 33 | if(end > beginning){ 34 | memcpy(destination, buffer + beginning, n); 35 | }else{ 36 | size_t first = min(size - beginning, n); 37 | memcpy(destination, buffer + beginning, first); 38 | memcpy(destination + first, buffer, n - first); 39 | } 40 | 41 | beginning = (beginning + n) % size; 42 | return n; 43 | } 44 | 45 | size_t RingBuffer::write(uint8_t* source, size_t n){ 46 | n = min(n, writeAvailable()); 47 | if(n == 0) return 0; 48 | 49 | if(beginning > end){ 50 | memcpy(buffer + end, source, n); 51 | }else{ 52 | size_t first = min(size - end, n); 53 | memcpy(buffer + end, source, first); 54 | memcpy(buffer, source + first, n - first); 55 | } 56 | 57 | end = (end + n) % size; 58 | return n; 59 | } 60 | 61 | size_t RingBuffer::skip(size_t n){ 62 | n = min(n, readAvailable()); 63 | if(n == 0) return 0; 64 | beginning = (beginning + n) % size; 65 | return n; 66 | } 67 | 68 | void RingBuffer::clear(){ 69 | beginning = end = 0; 70 | } 71 | -------------------------------------------------------------------------------- /src/Buffer/RingBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_RINGBUFFER_H 2 | #define CIRCUITOS_RINGBUFFER_H 3 | 4 | #include 5 | 6 | class RingBuffer { 7 | public: 8 | RingBuffer(size_t size); 9 | virtual ~RingBuffer(); 10 | 11 | size_t writeAvailable(); 12 | size_t readAvailable(); 13 | 14 | size_t read(uint8_t* destination, size_t n); 15 | size_t write(uint8_t* source, size_t n); 16 | 17 | template 18 | const T* peek(size_t offset = 0){ 19 | if(offset + sizeof(T) > readAvailable()) return nullptr; 20 | offset = (beginning + offset) % size; 21 | return (T*) (buffer + offset); 22 | } 23 | 24 | size_t skip(size_t n); 25 | 26 | void clear(); 27 | 28 | private: 29 | size_t size; 30 | 31 | size_t beginning = 0; 32 | size_t end = 0; 33 | 34 | uint8_t* buffer; 35 | }; 36 | 37 | 38 | #endif //CIRCUITOS_RINGBUFFER_H 39 | -------------------------------------------------------------------------------- /src/CircuitOS.h: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /src/Devices/AW9523.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_AW9523_H 2 | #define CIRCUITOS_AW9523_H 3 | 4 | #include 5 | #include 6 | 7 | class AW9523 { 8 | public: 9 | AW9523(TwoWire& Wire = ::Wire, uint8_t addr = 0x58); 10 | bool begin(); 11 | 12 | /** 13 | * Sends a software reset. Blocks for 50us after I2C transmission ends. 14 | */ 15 | void reset(); 16 | 17 | enum PinMode { IN, OUT, LED }; 18 | 19 | /** 20 | * Set pin mode: 21 | * IN - GPIO input 22 | * OUT - GPIO output 23 | * LED - GPIO output with current control. Use dim(pin, factor) function to set dimming factor. 24 | * @param pin Pin index 25 | * @param mode Pin mode 26 | */ 27 | void pinMode(uint8_t pin, PinMode mode); 28 | 29 | /** 30 | * Read pin input state. 31 | * @param pin Pin index 32 | * @return State - true for high, false for low 33 | */ 34 | bool read(uint8_t pin); 35 | 36 | /** 37 | * Set pin output state. 38 | * @param pin Pin index 39 | * @param state True for high, false for low 40 | */ 41 | void write(uint8_t pin, bool state); 42 | 43 | /** 44 | * Set LED dimming factor for pin. 45 | * @param pin Pin index 46 | * @param factor Dimming factor. Range 0-255 47 | */ 48 | void dim(uint8_t pin, uint8_t factor); 49 | 50 | /** 51 | * Enable or disable interrupt triggering for pin. 52 | * @param pin Pin index 53 | * @param enabled 54 | */ 55 | void setInterrupt(uint8_t pin, bool enabled); 56 | 57 | enum CurrentLimit : uint8_t { 58 | IMAX, // I_max 59 | IMAX_3Q, // I_max * 3/4 60 | IMAX_2Q, // I_max * 2/4 61 | IMAX_1Q // I_max * 1/4 62 | }; 63 | 64 | /** 65 | * Set LED drive current limit. Only applies to pins configured as LED output. I_max is 37mA. 66 | * IMAX - full I_max value (37mA) 67 | * IMAX_3Q - 3/4 * I_max 68 | * IMAX_2Q - 2/4 * I_max 69 | * IMAX_1Q - 1/4 * I_max 70 | */ 71 | void setCurrentLimit(CurrentLimit limit); 72 | 73 | private: 74 | TwoWire& Wire; 75 | const uint8_t addr; 76 | 77 | static const uint8_t dimmap[16]; 78 | 79 | struct Regs { 80 | uint8_t conf = 0; 81 | uint8_t dir[2] = { 0, 0 }; 82 | uint8_t output[2] = { 0, 0 }; 83 | uint8_t intr[2] = { 0, 0 }; 84 | uint8_t mode[2] = { 0xff, 0xff }; 85 | uint8_t dim[16] = { 0 }; 86 | } regs; 87 | 88 | uint8_t readReg(uint8_t reg) const; 89 | void writeReg(uint8_t reg, const uint8_t* data, size_t size) const; 90 | void writeReg(uint8_t reg, uint8_t data) const; 91 | }; 92 | 93 | 94 | #endif //CIRCUITOS_AW9523_H 95 | -------------------------------------------------------------------------------- /src/Devices/Matrix/DelayedMatrixOutput.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "DelayedMatrixOutput.h" 3 | #include "../../Loop/LoopManager.h" 4 | 5 | DelayedMatrixOutput::DelayedMatrixOutput(MatrixOutput* out, uint32_t pushDelay) : 6 | MatrixOutput(out->getWidth(), out->getHeight()), out(out), pushDelay(pushDelay), data(out->getWidth(), out->getHeight()){ 7 | 8 | } 9 | 10 | void DelayedMatrixOutput::init(){ 11 | LoopManager::addListener(this); 12 | } 13 | 14 | void DelayedMatrixOutput::push(const MatrixPixelData& data){ 15 | if(millis() - lastPush >= pushDelay){ 16 | out->push(data); 17 | lastPush = millis(); 18 | } else { 19 | this->data = data; 20 | pushNeeded = true; 21 | } 22 | } 23 | 24 | void DelayedMatrixOutput::loop(uint micros){ 25 | if(pushNeeded && millis() - lastPush > pushDelay){ 26 | lastPush = millis(); 27 | out->push(data); 28 | pushNeeded = false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Devices/Matrix/DelayedMatrixOutput.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_DELAYEDMATRIXOUTPUT_H 2 | #define CIRCUITOS_DELAYEDMATRIXOUTPUT_H 3 | 4 | #include 5 | #include "MatrixOutput.h" 6 | #include "../../Loop/LoopListener.h" 7 | 8 | class DelayedMatrixOutput : public MatrixOutput, public LoopListener { 9 | public: 10 | /** 11 | * @brief Constructor 12 | * @param out MatrixOutput to pushDelay 13 | * @param pushDelay Minimum pushDelay between push calls in ms, everything before the pushDelay time will not be pushed 14 | */ 15 | DelayedMatrixOutput(MatrixOutput* out, uint32_t pushDelay); 16 | void init() override; 17 | void push(const MatrixPixelData& data) override; 18 | void loop(uint micros) override; 19 | 20 | private: 21 | MatrixOutput* out; 22 | const uint32_t pushDelay; 23 | 24 | uint32_t lastPush = 0; 25 | bool pushNeeded = false; 26 | MatrixPixelData data; 27 | }; 28 | 29 | 30 | #endif //CIRCUITOS_DELAYEDMATRIXOUTPUT_H 31 | -------------------------------------------------------------------------------- /src/Devices/Matrix/IS31FL3731.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_IS31FL3731_H 2 | #define CIRCUITOS_IS31FL3731_H 3 | 4 | /* 5 | Modified version of Adafruits IS31FL3731 library (https://github.com/adafruit/Adafruit_IS31FL3731) 6 | 7 | This is a stripped-down version with no Adafruit-GFX library inheritance. 8 | */ 9 | 10 | #include 11 | #include "MatrixOutput.h" 12 | 13 | class IS31FL3731 : public MatrixOutput { 14 | public: 15 | IS31FL3731(TwoWire& Wire = ::Wire, uint8_t addr = 0x74); 16 | void init() override; 17 | void push(const MatrixPixelData& data) override; 18 | void setBrightness(uint8_t brightness) override; 19 | 20 | private: 21 | TwoWire& Wire; 22 | uint8_t addr; 23 | 24 | void audioSync(bool sync); 25 | 26 | /** 27 | * Write one byte to a register located in a given bank. 28 | * @param bank IS31 bank to write the register location 29 | * @param reg Offset into the bank to write 30 | * @param data Byte value 31 | */ 32 | void writeRegister8(uint8_t bank, uint8_t reg, uint8_t data); 33 | 34 | /** 35 | * Read one byte from a register located in a given bank. 36 | * @param bank IS31 bank to read the register location 37 | * @param reg Offset into the bank to read 38 | * @return 1 byte value 39 | */ 40 | uint8_t readRegister8(uint8_t bank, uint8_t reg); 41 | 42 | /** 43 | * Select bank for future reads and writes. 44 | * @param bank IS31 bank index 45 | */ 46 | void selectBank(uint8_t bank); 47 | 48 | MatrixPixelData currentState; 49 | 50 | }; 51 | 52 | 53 | #endif //CIRCUITOS_IS31FL3731_H 54 | -------------------------------------------------------------------------------- /src/Devices/Matrix/Matrix.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIX_H 2 | #define CIRCUITOS_MATRIX_H 3 | 4 | #include 5 | #include 6 | #include "MatrixOutput.h" 7 | #include "MatrixAnim.h" 8 | 9 | class Matrix { 10 | friend MatrixAnim; 11 | public: 12 | Matrix(MatrixOutput& output); 13 | void begin(); 14 | void clear(MatrixPixel color = MatrixPixel::Off); 15 | void push(); 16 | 17 | void setRotation(uint8_t rotation); 18 | uint8_t getRotation() const; 19 | 20 | /** 21 | * Set global brightness. 22 | * @param brightness 0-255 23 | */ 24 | void setBrightness(uint8_t brightness); 25 | uint8_t getBrightness() const; 26 | 27 | enum Font { BIG, SMALL }; 28 | 29 | /** 30 | * Set the drawChar and drawString font. BIG is 5x7, SMALL is 3x5 31 | * @param font BIG or SMALL 32 | */ 33 | void setFont(Font font); 34 | Font getFont() const; 35 | 36 | void drawPixel(uint16_t x, uint16_t y, const MatrixPixel& color); 37 | void drawPixel(uint16_t i, const MatrixPixel& color); 38 | 39 | void drawChar(int32_t x, int32_t y, unsigned char c, const MatrixPixel& color = { 255, 255, 255, 255 }); 40 | void drawString(int32_t x, int32_t y, const String &text, const MatrixPixel& color = { 255, 255, 255, 255 }); 41 | 42 | void drawBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* data, const MatrixPixel& color = { 255, 255, 255, 255 }); 43 | void drawBitmap(uint16_t x, uint16_t y, const MatrixPixelData& data); 44 | 45 | void startAnimation(MatrixAnim* animation); 46 | void stopAnimations(); 47 | std::unordered_set getAnimations(); 48 | 49 | const uint16_t getWidth() const; 50 | const uint16_t getHeight() const; 51 | 52 | private: 53 | MatrixOutput& output; 54 | MatrixPixelData data; 55 | 56 | const uint16_t width, height; 57 | 58 | uint8_t rotation = 0; 59 | Font font = BIG; 60 | 61 | std::unordered_set animations; 62 | void addAnim(MatrixAnim* anim); 63 | void removeAnim(MatrixAnim* anim); 64 | 65 | }; 66 | 67 | #endif -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixAnim.cpp: -------------------------------------------------------------------------------- 1 | #include "MatrixAnim.h" 2 | #include "Matrix.h" 3 | 4 | MatrixAnim::MatrixAnim(Matrix* matrix) : matrix(matrix){ 5 | if(matrix){ 6 | width = matrix->getWidth(); 7 | height = matrix->getHeight(); 8 | } 9 | } 10 | 11 | MatrixAnim::~MatrixAnim(){ 12 | 13 | } 14 | 15 | void MatrixAnim::start(){ 16 | if(matrix == nullptr) return; 17 | 18 | if(started) return; 19 | started = true; 20 | 21 | matrix->addAnim(this); 22 | 23 | onStart(); 24 | } 25 | 26 | void MatrixAnim::stop(){ 27 | if(!started) return; 28 | started = false; 29 | 30 | if(matrix){ 31 | matrix->removeAnim(this); 32 | } 33 | 34 | onStop(); 35 | } 36 | 37 | bool MatrixAnim::isStarted(){ 38 | return started; 39 | } 40 | 41 | void MatrixAnim::setX(int32_t x){ 42 | posX = x; 43 | } 44 | 45 | void MatrixAnim::setY(int32_t y){ 46 | posY = y; 47 | } 48 | 49 | void MatrixAnim::drawPixel(uint16_t x, uint16_t y, const MatrixPixel& color){ 50 | matrix->drawPixel(x + posX, y + posY, color); 51 | } 52 | 53 | void MatrixAnim::drawPixel(uint16_t i, const MatrixPixel& color){ 54 | drawPixel(i % width, i / width, color); 55 | } 56 | 57 | void MatrixAnim::drawChar(int32_t x, int32_t y, unsigned char c, const MatrixPixel& color){ 58 | matrix->drawChar(x + posX, y + posY, c, color); 59 | } 60 | 61 | void MatrixAnim::drawString(int32_t x, int32_t y, const String& text, const MatrixPixel& color){ 62 | matrix->drawString(x + posX, y + posY, text, color); 63 | } 64 | 65 | void MatrixAnim::drawBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* data, const MatrixPixel& color){ 66 | matrix->drawBitmap(x + posX, y + posY, width, height, data, color); 67 | } 68 | 69 | void MatrixAnim::drawBitmap(uint16_t x, uint16_t y, const MatrixPixelData& data){ 70 | matrix->drawBitmap(x + posX, y + posY, data); 71 | } 72 | 73 | void MatrixAnim::pushMatrix(){ 74 | matrix->push(); 75 | } 76 | 77 | void MatrixAnim::clear(){ 78 | for(uint32_t x = 0; x < width; x++){ 79 | for(uint32_t y = 0; y < height; y++){ 80 | drawPixel(x, y, {0, 0, 0, 0}); 81 | } 82 | } 83 | } 84 | 85 | void MatrixAnim::setWidth(uint32_t width){ 86 | MatrixAnim::width = width; 87 | } 88 | 89 | void MatrixAnim::setHeight(uint32_t height){ 90 | MatrixAnim::height = height; 91 | } 92 | 93 | int32_t MatrixAnim::getX() const{ 94 | return posX; 95 | } 96 | 97 | int32_t MatrixAnim::getY() const{ 98 | return posY; 99 | } 100 | 101 | uint32_t MatrixAnim::getWidth() const{ 102 | return width; 103 | } 104 | 105 | uint32_t MatrixAnim::getHeight() const{ 106 | return height; 107 | } 108 | 109 | void MatrixAnim::setMatrix(Matrix* matrix){ 110 | if(this->matrix == nullptr && width == 0 && height == 0){ 111 | width = matrix->getWidth(); 112 | height = matrix->getHeight(); 113 | } 114 | 115 | this->matrix = matrix; 116 | } 117 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixAnim.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXANIM_H 2 | #define CIRCUITOS_MATRIXANIM_H 3 | 4 | #include 5 | #include "MatrixPixel.h" 6 | 7 | class Matrix; 8 | 9 | class MatrixAnim { 10 | public: 11 | MatrixAnim(Matrix* matrix = nullptr); 12 | virtual ~MatrixAnim(); 13 | 14 | void start(); 15 | void stop(); 16 | virtual void reset() = 0; 17 | virtual void push() = 0; 18 | 19 | bool isStarted(); 20 | 21 | void setMatrix(Matrix* matrix); 22 | void setX(int32_t x); 23 | void setY(int32_t y); 24 | void setWidth(uint32_t width); 25 | void setHeight(uint32_t height); 26 | int32_t getX() const; 27 | int32_t getY() const; 28 | uint32_t getWidth() const; 29 | uint32_t getHeight() const; 30 | 31 | protected: 32 | void drawPixel(uint16_t x, uint16_t y, const MatrixPixel& color); 33 | void drawPixel(uint16_t i, const MatrixPixel& color); 34 | void drawChar(int32_t x, int32_t y, unsigned char c, const MatrixPixel& color = { 255, 255, 255, 255 }); 35 | void drawString(int32_t x, int32_t y, const String &text, const MatrixPixel& color = { 255, 255, 255, 255 }); 36 | void drawBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* data, const MatrixPixel& color = { 255, 255, 255, 255 }); 37 | void drawBitmap(uint16_t x, uint16_t y, const MatrixPixelData& data); 38 | void pushMatrix(); 39 | void clear(); 40 | 41 | virtual void onStart() = 0; 42 | virtual void onStop() = 0; 43 | 44 | private: 45 | Matrix* matrix = nullptr; 46 | bool started = false; 47 | 48 | int32_t posX = 0, posY = 0; 49 | uint32_t width = 0, height = 0; 50 | }; 51 | 52 | #endif -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixAnimGIF.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MatrixAnimGIF.h" 3 | #include "Matrix.h" 4 | #include 5 | 6 | MatrixAnimGIF::MatrixAnimGIF(fs::File file, Matrix* matrix) : MatrixAnim(matrix), gif(std::move(file)){ 7 | if(!gif) return; 8 | gif.nextFrame(); 9 | 10 | setHeight(gif.getHeight()); 11 | setWidth(gif.getWidth()); 12 | 13 | auto temp = gif.getLoopMode(); 14 | gif.setLoopMode(GIF::Single); 15 | while(gif.nextFrame()){ 16 | totalDuration+=gif.frameDuration(); 17 | } 18 | gif.reset(); 19 | gif.nextFrame(); 20 | gif.setLoopMode(temp); 21 | } 22 | 23 | MatrixAnimGIF::MatrixAnimGIF(fs::FileImpl* file, Matrix* matrix) : MatrixAnimGIF(fs::File(fs::FileImplPtr(file)), matrix){ 24 | 25 | } 26 | 27 | MatrixAnimGIF::~MatrixAnimGIF(){ 28 | stop(); 29 | } 30 | 31 | void MatrixAnimGIF::loop(uint delta){ 32 | if(!isStarted()){ 33 | LoopManager::removeListener(this); 34 | return; 35 | } 36 | 37 | if(!gif){ 38 | stop(); 39 | return; 40 | } 41 | 42 | uint32_t time = millis(); 43 | uint32_t elapsed = time - frameTime; 44 | if(elapsed >= gif.frameDuration()){ 45 | frameTime = time - (elapsed - gif.frameDuration()); 46 | 47 | if(!gif.nextFrame()){ 48 | stop(); 49 | reset(); 50 | return; 51 | } 52 | 53 | push(); 54 | } 55 | } 56 | 57 | void MatrixAnimGIF::push(){ 58 | if(!gif) return; 59 | 60 | drawBitmap(0, 0, gif.getFrame()); 61 | pushMatrix(); 62 | } 63 | 64 | void MatrixAnimGIF::onStart(){ 65 | if(!gif){ 66 | stop(); 67 | return; 68 | } 69 | 70 | startTime = millis(); 71 | frameTime = millis() - frameRemaining; 72 | 73 | push(); 74 | 75 | LoopManager::addListener(this); 76 | } 77 | 78 | void MatrixAnimGIF::onStop(){ 79 | LoopManager::removeListener(this); 80 | 81 | if(!gif) return; 82 | 83 | uint32_t frameLasted = millis() - frameTime; 84 | if(frameLasted < gif.frameDuration()){ 85 | frameRemaining = gif.frameDuration() - frameLasted; 86 | }else{ 87 | frameRemaining = 0; 88 | } 89 | 90 | frameTime = 0; 91 | } 92 | 93 | void MatrixAnimGIF::reset(){ 94 | if(!gif) return; 95 | 96 | gif.reset(); 97 | gif.nextFrame(); 98 | frameTime = millis(); 99 | frameRemaining = 0; 100 | push(); 101 | 102 | startTime = millis(); 103 | } 104 | 105 | GIF& MatrixAnimGIF::getGIF(){ 106 | return gif; 107 | } 108 | 109 | float MatrixAnimGIF::getCompletionPercentage(){ 110 | float elapsed = millis() - startTime; 111 | return (elapsed / totalDuration); 112 | } 113 | 114 | uint32_t MatrixAnimGIF::getLoopDuration() const{ 115 | return totalDuration; 116 | } 117 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixAnimGIF.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXANIMGIF_H 2 | #define CIRCUITOS_MATRIXANIMGIF_H 3 | 4 | #include 5 | #include 6 | #include "MatrixAnim.h" 7 | #include "../../Util/GIF.h" 8 | #include "../../Loop/LoopListener.h" 9 | 10 | class MatrixAnimGIF : public MatrixAnim, public LoopListener { 11 | public: 12 | MatrixAnimGIF(fs::File file, Matrix* matrix = nullptr); 13 | MatrixAnimGIF(fs::FileImpl* file, Matrix* matrix = nullptr); 14 | ~MatrixAnimGIF() override; 15 | 16 | GIF& getGIF(); 17 | 18 | void reset() override; 19 | 20 | void loop(uint time); 21 | 22 | void push() override; 23 | 24 | float getCompletionPercentage(); 25 | 26 | uint32_t getLoopDuration() const; 27 | 28 | protected: 29 | void onStart() override; 30 | void onStop() override; 31 | 32 | private: 33 | GIF gif; 34 | 35 | uint32_t frameTime = 0; 36 | uint32_t frameRemaining = 0; 37 | uint32_t startTime = 0; 38 | uint32_t totalDuration = 0; 39 | }; 40 | 41 | 42 | #endif //CIRCUITOS_MATRIXANIMGIF_H 43 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixOutput.cpp: -------------------------------------------------------------------------------- 1 | #include "MatrixOutput.h" 2 | 3 | MatrixOutput::MatrixOutput(uint16_t width, uint16_t height) : width(width), height(height){ 4 | 5 | } 6 | 7 | uint16_t MatrixOutput::getWidth() const{ 8 | return width; 9 | } 10 | 11 | uint16_t MatrixOutput::getHeight() const{ 12 | return height; 13 | } 14 | 15 | uint8_t MatrixOutput::getBrightness() const{ 16 | return brightness; 17 | } 18 | 19 | void MatrixOutput::setBrightness(uint8_t brightness){ 20 | MatrixOutput::brightness = brightness; 21 | } 22 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixOutput.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXOUTPUT_H 2 | #define CIRCUITOS_MATRIXOUTPUT_H 3 | 4 | #include 5 | #include "MatrixPixel.h" 6 | 7 | class MatrixOutput { 8 | public: 9 | 10 | MatrixOutput(uint16_t width, uint16_t height); 11 | 12 | virtual void init() = 0; 13 | virtual void push(const MatrixPixelData& data) = 0; 14 | 15 | uint16_t getWidth() const; 16 | uint16_t getHeight() const; 17 | virtual uint8_t getBrightness() const; 18 | virtual void setBrightness(uint8_t brightness); 19 | 20 | private: 21 | uint16_t width, height; 22 | uint8_t brightness = 255; 23 | 24 | }; 25 | 26 | 27 | #endif //CIRCUITOS_MATRIXOUTPUT_H 28 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixOutputBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "MatrixOutputBuffer.h" 2 | 3 | MatrixOutputBuffer::MatrixOutputBuffer(MatrixOutput* output) : MatrixOutput(output->getWidth(), output->getHeight()), output(output), 4 | data(output->getWidth(), output->getHeight()){ 5 | 6 | } 7 | 8 | void MatrixOutputBuffer::init(){ 9 | 10 | } 11 | 12 | void MatrixOutputBuffer::push(const MatrixPixelData& data){ 13 | this->data = data; 14 | output->push(data); 15 | } 16 | 17 | void MatrixOutputBuffer::_push(){ 18 | output->push(data); 19 | } 20 | 21 | void MatrixOutputBuffer::setBrightness(uint8_t brightness){ 22 | MatrixOutput::setBrightness(brightness); 23 | output->setBrightness(brightness); 24 | } 25 | 26 | const MatrixPixelData& MatrixOutputBuffer::getData(){ 27 | return data; 28 | } 29 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixOutputBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXOUTPUTBUFFER_H 2 | #define CIRCUITOS_MATRIXOUTPUTBUFFER_H 3 | 4 | #include 5 | #include "MatrixOutput.h" 6 | 7 | class MatrixOutputBuffer : public MatrixOutput { 8 | public: 9 | MatrixOutputBuffer(MatrixOutput* output); 10 | 11 | void init() override; 12 | void push(const MatrixPixelData& data) override; 13 | void _push(); 14 | 15 | void setBrightness(uint8_t brightness) override; 16 | 17 | const MatrixPixelData& getData(); 18 | 19 | private: 20 | MatrixOutput* output; 21 | MatrixPixelData data; 22 | 23 | }; 24 | 25 | 26 | #endif //CIRCUITOS_MATRIXOUTPUTBUFFER_H 27 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixPartOutput.cpp: -------------------------------------------------------------------------------- 1 | #include "MatrixPartOutput.h" 2 | 3 | MatrixPartOutput::MatrixPartOutput(MatrixOutputBuffer* output, int16_t width, int16_t height) : MatrixOutput(width, height), output(output){ 4 | 5 | } 6 | 7 | void MatrixPartOutput::init(){ 8 | 9 | } 10 | 11 | void IRAM_ATTR MatrixPartOutput::push(const MatrixPixelData& data){ 12 | MatrixPixelData whole = output->getData(); 13 | 14 | for(int x = 0; x < getWidth(); x++){ 15 | for(int y = 0; y < getHeight(); y++){ 16 | auto pixel = data.get(x, y); 17 | auto target = map(x, y); 18 | pixel.i = pixel.i * getBrightness() / 255; 19 | whole.set(target.first, target.second, pixel); 20 | } 21 | } 22 | 23 | output->push(whole); 24 | } -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixPartOutput.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXPARTOUTPUT_H 2 | #define CIRCUITOS_MATRIXPARTOUTPUT_H 3 | 4 | #include 5 | #include "MatrixOutputBuffer.h" 6 | 7 | class MatrixPartOutput : public MatrixOutput { 8 | public: 9 | MatrixPartOutput(MatrixOutputBuffer* output, int16_t width, int16_t height); 10 | 11 | void init() override; 12 | void push(const MatrixPixelData& data) override; 13 | 14 | protected: 15 | virtual std::pair map(uint16_t x, uint16_t y) = 0; 16 | 17 | private: 18 | MatrixOutputBuffer* output; 19 | 20 | }; 21 | 22 | 23 | #endif //CIRCUITOS_MATRIXPARTOUTPUT_H 24 | -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixPixel.cpp: -------------------------------------------------------------------------------- 1 | #include "MatrixPixel.h" 2 | #include "../../Display/Color.h" 3 | 4 | const MatrixPixel MatrixPixel::Red = { 255, 0, 0, 255 }; 5 | const MatrixPixel MatrixPixel::Green = { 0, 255, 0, 255 }; 6 | const MatrixPixel MatrixPixel::Blue = { 0, 0, 255, 255 }; 7 | const MatrixPixel MatrixPixel::Yellow = { 255, 255, 0, 255 }; 8 | const MatrixPixel MatrixPixel::Cyan = { 0, 255, 255, 255 }; 9 | const MatrixPixel MatrixPixel::Magenta = { 255, 0, 255, 255 }; 10 | const MatrixPixel MatrixPixel::White = { 255, 255, 255, 255 }; 11 | const MatrixPixel MatrixPixel::Black = { 0, 0, 0, 255 }; 12 | const MatrixPixel MatrixPixel::Off = { 0, 0, 0, 0 }; 13 | 14 | bool operator==(const MatrixPixel& a, const MatrixPixel& b){ 15 | return a.r == b.r 16 | && a.g == b.g 17 | && a.b == b.b 18 | && a.i == b.i; 19 | } 20 | 21 | bool operator!=(const MatrixPixel& a, const MatrixPixel& b){ 22 | return a.r != b.r 23 | || a.g != b.g 24 | || a.b != b.b 25 | || a.i != b.i; 26 | } 27 | 28 | MatrixPixelData::MatrixPixelData(uint16_t width, uint16_t height) : width(width), height(height){ 29 | data.resize(width); 30 | for(int x = 0; x < width; x++){ 31 | data[x].resize(height); 32 | } 33 | } 34 | 35 | MatrixPixelData::MatrixPixelData(const GIF::Frame& frame) : MatrixPixelData(frame.getWidth(), frame.getHeight()){ 36 | for(int x = 0; x < width; x++){ 37 | for(int y = 0; y < height; y++){ 38 | Pixel pix = frame.getData()[y * frame.getWidth() + x]; 39 | data[x][y] = { pix.r, pix.g, pix.b, 255 }; 40 | } 41 | } 42 | } 43 | 44 | MatrixPixelData::MatrixPixelData(const MatrixPixelData& other){ 45 | *this = other; 46 | } 47 | 48 | MatrixPixelData& IRAM_ATTR MatrixPixelData::operator=(const MatrixPixelData& other){ 49 | if(&other == this) return *this; 50 | 51 | width = other.width; 52 | height = other.height; 53 | 54 | data.resize(width); 55 | for(int x = 0; x < width; x++){ 56 | data[x].resize(height); 57 | memcpy(data[x].data(), other.data[x].data(), height * sizeof(MatrixPixel)); 58 | } 59 | 60 | return *this; 61 | } 62 | 63 | void MatrixPixelData::set(uint16_t x, uint16_t y, const MatrixPixel& pixel){ 64 | if(x >= width || y >= height) return; 65 | data[x][y] = pixel; 66 | } 67 | 68 | MatrixPixel MatrixPixelData::get(uint16_t x, uint16_t y) const{ 69 | if(x >= width || y >= height) return { }; 70 | return data[x][y]; 71 | } 72 | 73 | void MatrixPixelData::clear(const MatrixPixel& color){ 74 | for(int x = 0; x < width; x++){ 75 | data[x] = std::vector(height, color); 76 | } 77 | } 78 | 79 | uint16_t MatrixPixelData::getWidth() const{ 80 | return width; 81 | } 82 | 83 | uint16_t MatrixPixelData::getHeight() const{ 84 | return height; 85 | } 86 | 87 | MatrixPixelData::Column MatrixPixelData::operator[](uint16_t x){ 88 | return Column(*this, x); 89 | } 90 | 91 | MatrixPixelData::Column::Column(MatrixPixelData& parent, uint16_t x) : parent(parent), x(x){ } 92 | 93 | MatrixPixel& MatrixPixelData::Column::operator[](uint16_t y) const{ 94 | if(x >= parent.width || y >= parent.height) throw std::exception(); 95 | return parent.data[x][y]; 96 | } -------------------------------------------------------------------------------- /src/Devices/Matrix/MatrixPixel.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MATRIXPIXEL_H 2 | #define CIRCUITOS_MATRIXPIXEL_H 3 | 4 | #include 5 | #include 6 | #include "../../Util/GIF.h" 7 | 8 | struct MatrixPixel { 9 | uint8_t r, g, b, i; 10 | static const MatrixPixel Red; 11 | static const MatrixPixel Green; 12 | static const MatrixPixel Blue; 13 | static const MatrixPixel Magenta; 14 | static const MatrixPixel Cyan; 15 | static const MatrixPixel Yellow; 16 | static const MatrixPixel White; 17 | static const MatrixPixel Black; 18 | static const MatrixPixel Off; 19 | }; 20 | 21 | bool operator==(const MatrixPixel& a, const MatrixPixel& b); 22 | bool operator!=(const MatrixPixel& a, const MatrixPixel& b); 23 | 24 | class MatrixPixelData { 25 | public: 26 | MatrixPixelData(uint16_t width, uint16_t height); 27 | MatrixPixelData(const GIF::Frame& gifFrame); 28 | MatrixPixelData(const MatrixPixelData& other); 29 | MatrixPixelData& operator=(const MatrixPixelData& other); 30 | 31 | uint16_t getWidth() const; 32 | uint16_t getHeight() const; 33 | 34 | void set(uint16_t x, uint16_t y, const MatrixPixel& pixel); 35 | MatrixPixel get(uint16_t x, uint16_t y) const; 36 | 37 | void clear(const MatrixPixel& color = { 0, 0, 0, 0 }); 38 | 39 | class Column { 40 | public: 41 | Column(MatrixPixelData& parent, uint16_t x); 42 | MatrixPixel& operator[](uint16_t y) const; 43 | 44 | private: 45 | MatrixPixelData& parent; 46 | uint16_t x; 47 | }; 48 | 49 | Column operator[](uint16_t x); 50 | 51 | private: 52 | uint16_t width, height; 53 | std::vector> data; 54 | 55 | }; 56 | 57 | #endif //CIRCUITOS_MATRIXPIXEL_H 58 | -------------------------------------------------------------------------------- /src/Devices/Motion/ICM20948.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_ICM20948 4 | #include "ICM20948.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Devices/Motion/ICM20948.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_ICM20948_H 2 | #define CIRCUITOS_ICM20948_H 3 | 4 | #include "../../Motion/MPU.h" 5 | 6 | class ICM_20948_I2C; 7 | 8 | class ICM20948 : public MPU { 9 | public: 10 | explicit ICM20948(int sda, int scl); 11 | 12 | bool readSensor() override; 13 | void begin() override; 14 | void calibrate() override; 15 | 16 | void calibrateGyro(uint8_t loops = 100); 17 | void calibrateAccel(uint8_t loops = 100); 18 | 19 | private: 20 | int sda = -1; 21 | int scl = -1; 22 | float accelScale = 1; 23 | float gyroScale = 1; 24 | vec3f gyroBias = { 0, 0, 0 }; 25 | 26 | ICM_20948_I2C* icm = nullptr; 27 | 28 | bool readICMData(bool waitReady = false, bool adjust = true); 29 | void load(); 30 | }; 31 | 32 | 33 | #endif //CIRCUITOS_ICM20948_H 34 | -------------------------------------------------------------------------------- /src/Devices/Motion/MPU6050_CM.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_MPU6050 4 | #include "MPU6050_CM.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Devices/Motion/MPU6050_CM.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MPU6050_CM_H 2 | #define CIRCUITOS_MPU6050_CM_H 3 | 4 | #include "../../Motion/MPU.h" 5 | 6 | class MPU6050; 7 | 8 | class MPU6050_CM : public MPU { 9 | public: 10 | MPU6050_CM(int sda, int scl); 11 | 12 | bool readSensor() override; 13 | void begin() override; 14 | void calibrate() override; 15 | 16 | void calibrateGyro(uint8_t loops = 100); 17 | void calibrateAccel(uint8_t loops = 100); 18 | 19 | private: 20 | int sda = -1; 21 | int scl = -1; 22 | float accelScale = 1; 23 | float gyroScale = 1; 24 | vec3f gyroBias = { 0, 0, 0 }; 25 | 26 | MPU6050* mpu = nullptr; 27 | 28 | bool readData(bool waitReady = false, bool adjust = true); 29 | }; 30 | 31 | 32 | #endif //CIRCUITOS_MPU6050_CM_H 33 | -------------------------------------------------------------------------------- /src/Devices/SerialFlash/SerialFlashFileAdapter.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_SERIALFLASH 4 | #include "SerialFlashFileAdapter.impl" 5 | #endif 6 | -------------------------------------------------------------------------------- /src/Devices/SerialFlash/SerialFlashFileAdapter.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SERIALFLASHFILEADAPTER_H 2 | #define CIRCUITOS_SERIALFLASHFILEADAPTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class SerialFlashFile; 10 | /** 11 | * Class to adapt SerialFlashFile class from the SerialFlash lib by Paul Stoffregen (https://github.com/PaulStoffregen/SerialFlash) 12 | * to the File interface from esp8266/esp32 FS core. 13 | */ 14 | class SerialFlashFileAdapter : public fs::FileImpl { 15 | public: 16 | SerialFlashFileAdapter(SerialFlashFile* _file); 17 | SerialFlashFileAdapter(const char* path); 18 | virtual ~SerialFlashFileAdapter(); 19 | 20 | size_t write(const uint8_t* buf, size_t size) override; 21 | size_t read(uint8_t* buf, size_t size) override; 22 | void flush() override; 23 | bool seek(uint32_t pos, fs::SeekMode mode) override; 24 | size_t position() const override; 25 | size_t size() const override; 26 | void close() override; 27 | time_t getLastWrite() override; 28 | const char* name() const override; 29 | bool isDirectory(); 30 | fs::FileImplPtr openNextFile(const char* mode) override; 31 | void rewindDirectory() override; 32 | operator bool() override; 33 | 34 | bool seek(uint32_t pos); 35 | size_t write(uint8_t data); 36 | int read(); 37 | int peek(); 38 | int available(); 39 | 40 | private: 41 | SerialFlashFile* file; 42 | }; 43 | 44 | 45 | #endif //CIRCUITOS_SERIALFLASHFILEADAPTER_H 46 | -------------------------------------------------------------------------------- /src/Devices/SerialFlash/SerialFlashFileAdapter.impl: -------------------------------------------------------------------------------- 1 | #include "SerialFlashFileAdapter.h" 2 | #include 3 | 4 | SerialFlashFileAdapter::SerialFlashFileAdapter(SerialFlashFile* _file) : file(_file){ 5 | } 6 | 7 | SerialFlashFileAdapter::SerialFlashFileAdapter(const char* path) : file(new SerialFlashFile()){ 8 | while(!SerialFlash.ready()); 9 | *file = SerialFlash.open(path); 10 | 11 | if(!(*file)){ 12 | file->close(); 13 | Serial.printf("BAD FILE %s\n", path); 14 | } 15 | delay(5); 16 | } 17 | 18 | SerialFlashFileAdapter::~SerialFlashFileAdapter(){ 19 | close(); 20 | delete file; 21 | } 22 | 23 | size_t SerialFlashFileAdapter::write(uint8_t data){ 24 | return file->write((void*)data, 1); 25 | } 26 | 27 | size_t SerialFlashFileAdapter::write(const uint8_t* buf, size_t size){ 28 | return file->write(buf, size); 29 | } 30 | 31 | int SerialFlashFileAdapter::available(){ 32 | return file->available(); 33 | } 34 | 35 | int SerialFlashFileAdapter::read(){ 36 | int output; 37 | if(file->read((void*)output, 1) == 0) return 0; 38 | return output; 39 | } 40 | 41 | int SerialFlashFileAdapter::peek(){ 42 | int output; 43 | if(file->read((void*)output, 1) == 0) return 0; 44 | file->seek(file->position() - 1); 45 | return output; 46 | } 47 | 48 | void SerialFlashFileAdapter::flush(){ 49 | 50 | } 51 | 52 | bool SerialFlashFileAdapter::seek(uint32_t pos, fs::SeekMode mode){ 53 | switch(mode){ 54 | case fs::SeekSet: 55 | file->seek(pos); 56 | break; 57 | case fs::SeekEnd: 58 | file->seek(file->size() - pos - 1); 59 | break; 60 | case fs::SeekCur: 61 | file->seek(file->position() + pos); 62 | break; 63 | } 64 | return true; 65 | } 66 | 67 | bool SerialFlashFileAdapter::seek(uint32_t pos){ 68 | return seek(pos, fs::SeekSet); 69 | } 70 | 71 | size_t SerialFlashFileAdapter::read(uint8_t* buf, size_t size){ 72 | return file->read(buf, size); 73 | } 74 | 75 | size_t SerialFlashFileAdapter::position() const{ 76 | return file->position(); 77 | } 78 | 79 | size_t SerialFlashFileAdapter::size() const{ 80 | return file->size(); 81 | } 82 | 83 | void SerialFlashFileAdapter::close(){ 84 | file->close(); 85 | } 86 | 87 | SerialFlashFileAdapter::operator bool(){ 88 | // Serial.printf("file ok: %d", bool(*file)); 89 | // delay(5); 90 | return bool(*file); 91 | } 92 | 93 | bool SerialFlashFileAdapter::isDirectory(){ 94 | return false; 95 | } 96 | 97 | time_t SerialFlashFileAdapter::getLastWrite(){ 98 | return 0; 99 | } 100 | 101 | const char* SerialFlashFileAdapter::name() const { 102 | return nullptr; 103 | } 104 | 105 | void SerialFlashFileAdapter::rewindDirectory() { 106 | 107 | } 108 | 109 | fs::FileImplPtr SerialFlashFileAdapter::openNextFile(const char* mode){ 110 | return fs::FileImplPtr(); 111 | } 112 | -------------------------------------------------------------------------------- /src/Devices/ShiftOutput.cpp: -------------------------------------------------------------------------------- 1 | #include "ShiftOutput.h" 2 | 3 | #define PERIOD 1 4 | #define LH(pin) do { digitalWrite(pin, LOW); digitalWrite(pin, HIGH); } while(0) 5 | #define HL(pin) do { digitalWrite(pin, HIGH); digitalWrite(pin, LOW); } while(0) 6 | 7 | const float ShiftOutput::nopCycles = (float) ESP.getCpuFreqMHz() / 3.0f; 8 | 9 | ShiftOutput::ShiftOutput(uint8_t clk, const std::vector& dataPins) : clockPin(clk), dataPins(dataPins), size(dataPins.size()){ 10 | state.resize(size); 11 | } 12 | 13 | ShiftOutput::ShiftOutput(uint8_t clockPin, uint8_t dataPin) : ShiftOutput(clockPin, std::vector{ dataPin }){ 14 | 15 | } 16 | 17 | void ShiftOutput::begin(){ 18 | pinMode(clockPin, OUTPUT); 19 | digitalWrite(clockPin, LOW); 20 | 21 | for(auto data : this->dataPins){ 22 | pinMode(data, OUTPUT); 23 | } 24 | 25 | send(this->state); 26 | } 27 | 28 | void ShiftOutput::set(uint16_t _pin, bool state){ 29 | uint8_t pin = _pin % 8; 30 | uint8_t index = (_pin - pin) / 8; 31 | 32 | set(index, pin, state); 33 | } 34 | 35 | void ShiftOutput::set(uint8_t index, uint8_t pin, bool state){ 36 | if(index >= size || pin >= 8) return; 37 | 38 | this->state[index][pin] = state; 39 | 40 | send(this->state); 41 | } 42 | 43 | void ShiftOutput::set(uint8_t index, const std::array& state){ 44 | if(index >= size) return; 45 | 46 | this->state[index] = state; 47 | 48 | send(this->state); 49 | } 50 | 51 | void ShiftOutput::set(const std::vector>& state){ 52 | if(state.size() != size) return; 53 | 54 | this->state = state; 55 | 56 | send(this->state); 57 | } 58 | 59 | void ShiftOutput::setAll(bool state){ 60 | for(auto& shift : this->state){ 61 | shift.fill(state); 62 | } 63 | 64 | send(this->state); 65 | } 66 | 67 | void IRAM_ATTR ShiftOutput::send(const std::vector>& state){ 68 | digitalWrite(clockPin, LOW); 69 | 70 | for(int i = 0; i < 8; i++){ 71 | for(int j = 0; j < size; j++){ 72 | if(state[j][7 - i]){ 73 | GPIO.out_w1ts = ((uint32_t) 1) << dataPins[j]; 74 | }else{ 75 | GPIO.out_w1tc = ((uint32_t) 1) << dataPins[j]; 76 | } 77 | } 78 | 79 | HL(clockPin); 80 | } 81 | } 82 | 83 | bool ShiftOutput::get(uint16_t _pin){ 84 | uint8_t pin = _pin % 8; 85 | uint8_t index = (_pin - pin) / 8; 86 | 87 | return get(index, pin); 88 | } 89 | 90 | bool ShiftOutput::get(uint8_t index, uint8_t pin){ 91 | if(index >= size || pin >= 8) return false; 92 | return state[index][pin]; 93 | } 94 | 95 | std::array ShiftOutput::get(uint8_t index){ 96 | if(index >= size) return { }; 97 | return state[index]; 98 | }; 99 | 100 | std::vector> ShiftOutput::get(){ 101 | return state; 102 | } 103 | 104 | void IRAM_ATTR ShiftOutput::delayNanos(uint32_t nanos){ 105 | uint32_t cycles = round((float) nanos / nopCycles); 106 | for(volatile uint32_t i = 0; i < cycles; i++); 107 | } 108 | -------------------------------------------------------------------------------- /src/Devices/ShiftOutput.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SHIFTOUTPUT_H 2 | #define CIRCUITOS_SHIFTOUTPUT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class ShiftOutput { 9 | public: 10 | ShiftOutput(uint8_t clockPin, const std::vector& dataPins); 11 | ShiftOutput(uint8_t clockPin, uint8_t dataPin); 12 | 13 | void begin(); 14 | 15 | void set(uint16_t pin, bool state); 16 | void set(uint8_t index, uint8_t pin, bool state); 17 | void set(uint8_t index, const std::array& state); 18 | void set(const std::vector>& state); 19 | void setAll(bool state); 20 | 21 | bool get(uint16_t pin); 22 | bool get(uint8_t index, uint8_t pin); 23 | std::array get(uint8_t index); 24 | std::vector> get(); 25 | 26 | static void IRAM_ATTR delayNanos(uint32_t nanos); 27 | 28 | private: 29 | const uint8_t clockPin; 30 | const std::vector dataPins; 31 | const uint8_t size; 32 | 33 | std::vector> state; 34 | 35 | static const float nopCycles; 36 | 37 | public: 38 | void IRAM_ATTR send(const std::vector>& newState); 39 | 40 | }; 41 | 42 | 43 | #endif //CIRCUITOS_SHIFTOUTPUT_H 44 | -------------------------------------------------------------------------------- /src/Display/AnimatedSprite.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_ANIMATEDSPRITE_H 2 | #define CIRCUITOS_ANIMATEDSPRITE_H 3 | 4 | 5 | #include 6 | #include "../Loop/LoopListener.h" 7 | #include "Sprite.h" 8 | #include "../UI/CustomElement.h" 9 | #include "../UI/SpriteElement.h" 10 | #include 11 | 12 | class AnimatedSprite { 13 | public: 14 | AnimatedSprite(Sprite* canvas, fs::File file, bool compressed = false); 15 | virtual ~AnimatedSprite(); 16 | 17 | void push(); 18 | void reset(); 19 | void setLoopDoneCallback(void (*callback)()); 20 | 21 | /** 22 | * Checks if it's time for a new frame, and if so, loads it. This will fire the LoopDone callback. 23 | * The callback will be called after last frame is over. 24 | * @return True of a new frame is ready, false otherwise. 25 | */ 26 | bool checkFrame(); 27 | 28 | /** 29 | * @brief Function to load the next frame of the GIF, if available. 30 | * 31 | * @return true New frame is loaded successfully. 32 | * @return false Reached the end of GIF, no more frames available. 33 | */ 34 | bool nextFrame(); 35 | 36 | void setX(int16_t x); 37 | void setY(int16_t y); 38 | void setXY(int16_t x, int16_t y); 39 | 40 | uint16_t getWidth() const; 41 | uint16_t getHeight() const; 42 | 43 | void setLoop(bool loop); 44 | void setMaskingColor(Color maskingColor); 45 | 46 | void setSwapBytes(bool swapBytes); 47 | 48 | void start(); 49 | void stop(); 50 | 51 | private: 52 | Sprite* canvas; 53 | fs::File file; 54 | bool loop = false; 55 | bool swapBytes = false; 56 | Color maskingColor = TFT_TRANSPARENT; 57 | size_t dataStart; 58 | uint32_t currentFrameTime = 0; 59 | 60 | int16_t x = 0; 61 | int16_t y = 0; 62 | uint16_t width; 63 | uint16_t height; 64 | 65 | struct Header { 66 | uint16_t width; 67 | uint16_t height; 68 | uint16_t noFrames; 69 | uint8_t flags; 70 | } __attribute__((packed)); 71 | 72 | class Table { 73 | public: 74 | explicit Table(File file); 75 | ~Table(); 76 | 77 | Color getColor(uint8_t i) const; 78 | uint8_t getNoColors() const; 79 | 80 | private: 81 | uint8_t noColors; 82 | Color* colors; 83 | } *table = nullptr; 84 | 85 | struct Frame { 86 | uint8_t* data = nullptr; 87 | uint16_t duration = 0; 88 | } gifFrame; 89 | 90 | uint16_t noFrames = 0; 91 | uint16_t currentFrame = 0; 92 | uint8_t flags; 93 | 94 | bool onLastFrame = false; 95 | void (*loopDoneCallback)() = nullptr; 96 | bool alerted = false; 97 | bool playing = false; 98 | 99 | bool compressed = false; 100 | }; 101 | 102 | 103 | #endif //CIRCUITOS_ANIMATEDSPRITE_H 104 | -------------------------------------------------------------------------------- /src/Display/Color.cpp: -------------------------------------------------------------------------------- 1 | #include "Color.h" 2 | 3 | const Pixel Pixel::Red = { 255, 0, 0}; 4 | const Pixel Pixel::Green = { 0, 255, 0}; 5 | const Pixel Pixel::Blue = { 0, 0, 255}; 6 | const Pixel Pixel::Yellow = { 255, 255, 0}; 7 | const Pixel Pixel::Cyan = { 0, 255, 255}; 8 | const Pixel Pixel::Magenta = { 255, 0, 255}; 9 | const Pixel Pixel::White = { 255, 255, 255}; 10 | const Pixel Pixel::Black = { 0, 0, 0}; 11 | 12 | Color C_RGB(uint8_t r, uint8_t g, uint8_t b){ 13 | return (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); 14 | } 15 | 16 | Color C_HEX(uint32_t hex){ 17 | return C_RGB( 18 | (hex >> 16) & 0xFF, 19 | (hex >> 8) & 0xFF, 20 | hex & 0xFF 21 | ); 22 | } -------------------------------------------------------------------------------- /src/Display/Color.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_COLOR_H 2 | #define CIRCUITOS_COLOR_H 3 | 4 | #include 5 | 6 | typedef uint16_t Color; 7 | 8 | struct Pixel { uint8_t r, g, b; 9 | static const Pixel Red; 10 | static const Pixel Green; 11 | static const Pixel Blue; 12 | static const Pixel Magenta; 13 | static const Pixel Cyan; 14 | static const Pixel Yellow; 15 | static const Pixel White; 16 | static const Pixel Black; 17 | 18 | bool operator==(const Pixel& right) const { 19 | return r == right.r && g == right.g && b == right.b; 20 | } 21 | }; 22 | 23 | Color C_RGB(uint8_t r, uint8_t g, uint8_t b); 24 | 25 | Color C_HEX(uint32_t hex); 26 | 27 | #endif //CIRCUITOS_COLOR_H 28 | -------------------------------------------------------------------------------- /src/Display/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.h" 2 | 3 | Display::Display(uint16_t width, uint16_t height, int8_t blPin, int8_t rotation, bool mirror) : tft(), blPin(blPin), 4 | width(width), height(height), baseSprite(new Sprite(&tft, width, height)), rotation(rotation), mirror(mirror){ 5 | 6 | if(baseSprite == nullptr){ 7 | Serial.println("Base sprite init error"); 8 | } 9 | } 10 | 11 | void Display::begin() 12 | { 13 | if(blPin != -1) 14 | { 15 | //ledcSetup(0, 2000, 8); 16 | //ledcAttachPin(blPin, 0); 17 | pinMode(blPin, OUTPUT); 18 | digitalWrite(blPin, HIGH); 19 | } 20 | 21 | #ifndef CIRCUITOS_LOVYANGFX 22 | tft.setAttribute(PSRAM_ENABLE, false); 23 | #endif 24 | tft.init(); 25 | tft.setRotation(3); 26 | #ifndef CIRCUITOS_LOVYANGFX 27 | tft.setAttribute(PSRAM_ENABLE, false); 28 | #else 29 | tft.setColorDepth(16); 30 | #endif 31 | 32 | tft.writecommand(17); //wakeup command in case display driver is in sleep mode 33 | 34 | tft.invertDisplay(0); 35 | if(rotation != 1){ 36 | tft.setRotation(rotation); 37 | } 38 | tft.fillScreen(TFT_BLACK); 39 | baseSprite->clear(TFT_BLACK); 40 | swapBytes(true); 41 | 42 | if(mirror){ 43 | mirrorBuffer = static_cast(malloc(sizeof(uint16_t) * getWidth() * getHeight())); 44 | } 45 | } 46 | void Display::setPower(bool power){ 47 | if(blPin != -1){ 48 | digitalWrite(blPin, power ? HIGH : LOW); 49 | } 50 | } 51 | 52 | void Display::commit(){ 53 | if(mirror){ 54 | for(int i = 0; i < getWidth(); i++){ 55 | for(int j = 0; j < getHeight(); j++){ 56 | mirrorBuffer[j * getWidth() + i] = baseSprite->readPixel(getWidth() - i - 1, j); 57 | } 58 | } 59 | 60 | baseSprite->drawIcon(mirrorBuffer, 0, 0, getWidth(), getHeight()); 61 | } 62 | #ifdef CIRCUITOS_LOVYANGFX 63 | tft.startWrite(); 64 | #ifdef CIRCUITOS_LOVYANGFX_ZOOM 65 | baseSprite->pushRotateZoom(width + 1, height + 1, 0, CIRCUITOS_LOVYANGFX_ZOOM, CIRCUITOS_LOVYANGFX_ZOOM); 66 | #else 67 | baseSprite->push(); 68 | #endif 69 | tft.endWrite(); 70 | #else 71 | baseSprite->push(); 72 | #endif 73 | } 74 | 75 | void Display::clear(uint32_t color){ 76 | baseSprite->clear(color); 77 | } 78 | 79 | TFT_eSPI* Display::getTft(){ 80 | return &tft; 81 | } 82 | 83 | Sprite* Display::getBaseSprite(){ 84 | return baseSprite; 85 | } 86 | 87 | uint Display::getWidth() const{ 88 | return width; 89 | } 90 | 91 | uint Display::getHeight() const{ 92 | return height; 93 | } 94 | 95 | void Display::swapBytes(bool swap){ 96 | static_cast(baseSprite)->setSwapBytes(swap); 97 | } 98 | -------------------------------------------------------------------------------- /src/Display/Display.h: -------------------------------------------------------------------------------- 1 | #ifndef SWTEST_DISPLAY_H 2 | #define SWTEST_DISPLAY_H 3 | #include "../../Setup.hpp" 4 | 5 | #ifdef CIRCUITOS_LOVYANGFX 6 | #include "LovyanGFX_setup.h" 7 | #else 8 | #include 9 | #endif 10 | #include "Sprite.h" 11 | 12 | class Sprite; 13 | 14 | class Display { 15 | public: 16 | 17 | /** 18 | * 19 | * @param width 20 | * @param height 21 | * @param blPin 22 | * @param rotation 23 | * @param mirror Horizontal display mirroring. WARNING: decimates the framerate and uses width*height*2 bytes of RAM 24 | */ 25 | Display(uint16_t width, uint16_t height, int8_t blPin = -1, int8_t rotation = -1, bool mirror = false); 26 | void begin(); 27 | void commit(); 28 | void clear(uint32_t color); 29 | 30 | TFT_eSPI* getTft(); 31 | Sprite* getBaseSprite(); 32 | 33 | /** 34 | * Sets the backlight power. 35 | * @param power 36 | */ 37 | void setPower(bool power); 38 | 39 | uint getWidth() const; 40 | 41 | uint getHeight() const; 42 | 43 | void swapBytes(bool swap); 44 | 45 | private: 46 | TFT_eSPI tft; 47 | uint16_t width; 48 | uint16_t height; 49 | int8_t blPin; 50 | int8_t rotation; 51 | 52 | uint16_t* mirrorBuffer = nullptr; 53 | bool mirror; 54 | 55 | Sprite* baseSprite = nullptr; 56 | }; 57 | 58 | 59 | #endif //SWTEST_DISPLAY_H 60 | -------------------------------------------------------------------------------- /src/Display/GIFAnimatedSprite.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_GIFANIMATEDSPRITE_H 2 | #define CIRCUITOS_GIFANIMATEDSPRITE_H 3 | 4 | #include 5 | #include "../Loop/LoopListener.h" 6 | #include "Sprite.h" 7 | #include 8 | #include 9 | #include "../Util/GIF.h" 10 | 11 | class GIFAnimatedSprite : private LoopListener { 12 | public: 13 | GIFAnimatedSprite(Sprite* parentSprite, const fs::File& gifFile); 14 | virtual ~GIFAnimatedSprite(); 15 | void loop(uint micros) override; 16 | 17 | void start(); 18 | void stop(); 19 | 20 | /** 21 | * @brief Pushes the current GIF frame onto the parentSprite defined in the constructor, 22 | * on the position selected by the setter/getters 23 | */ 24 | void push() const; 25 | 26 | /** 27 | * @brief Pushes the current GIF frame onto the sprite and position provided, overriding the previously set parentSprite and position. 28 | * @param sprite Sprite to be pushed on. 29 | * @param x, y Relative position on the sprite. 30 | */ 31 | void push(Sprite* sprite, int x, int y, Color maskingColor = TFT_TRANSPARENT) const; 32 | 33 | #ifdef CIRCUITOS_LOVYANGFX 34 | 35 | /** 36 | * @brief Applies rotation and pushes the current GIF frame onto the sprite and position provided, overriding the previously set parentSprite and position. 37 | * @param sprite Sprite to be pushed on. 38 | * @param x, y Relative position on the sprite. 39 | * @param rotation Rotation to be applied to the sprite, in degrees. [0-360] 40 | */ 41 | void pushRotate(Sprite* sprite, int x, int y, float rot, Color maskingColor = TFT_TRANSPARENT) const; 42 | #endif 43 | void reset(); 44 | 45 | int getX() const; 46 | void setX(int x); 47 | int getY() const; 48 | void setY(int y); 49 | void setXY(int x, int y); 50 | uint16_t getWidth() const; 51 | uint16_t getHeight() const; 52 | 53 | GIF::LoopMode getLoopMode() const; 54 | void setLoopMode(GIF::LoopMode loopMode); 55 | uint32_t getLoopCount() const; 56 | 57 | void setLoopDoneCallback(std::function loopDoneCallback); 58 | void setScale(uint8_t scale); 59 | 60 | private: 61 | GIF gif; 62 | Sprite* parentSprite = nullptr; 63 | int16_t x = 0, y = 0; 64 | 65 | uint32_t frameCounter = 0; 66 | uint32_t loopCount = 0; 67 | 68 | uint8_t scale = 1; 69 | 70 | std::function loopDoneCallback; 71 | }; 72 | 73 | 74 | #endif //CIRCUITOS_GIFANIMATEDSPRITE_H 75 | -------------------------------------------------------------------------------- /src/Display/LovyanGFX_setup.h: -------------------------------------------------------------------------------- 1 | #ifndef LOVYANGFXTEST_DISPLAYSETTINGS_H 2 | #define LOVYANGFXTEST_DISPLAYSETTINGS_H 3 | 4 | #define LGFX_USE_V1 5 | #define ARDUINO_LOLIN_D32_PRO 6 | 7 | #include 8 | 9 | #ifndef LOVYAN_HOST 10 | #define LOVYAN_HOST VSPI_HOST 11 | #endif 12 | 13 | namespace lgfx { 14 | class LGFX : public lgfx::LGFX_Device { 15 | private: 16 | 17 | Panel_Device* panel = nullptr; 18 | Bus_SPI* bus = nullptr; 19 | 20 | public: 21 | 22 | LGFX(void){ 23 | #ifndef LOVYAN_MANUAL 24 | bus = new lgfx::Bus_SPI(); 25 | panel = new lgfx::LOVYAN_PANEL(); 26 | 27 | { 28 | auto cfg = bus->config(); 29 | 30 | 31 | cfg.spi_host = LOVYAN_HOST; 32 | cfg.spi_mode = 0; 33 | cfg.freq_write = LOVYAN_FREQ; 34 | cfg.freq_read = LOVYAN_FREQ; 35 | cfg.spi_3wire = false; 36 | cfg.use_lock = true; 37 | cfg.dma_channel = 1; 38 | cfg.pin_sclk = LOVYAN_SCK; 39 | cfg.pin_mosi = LOVYAN_MOSI; 40 | cfg.pin_miso = LOVYAN_MISO; 41 | cfg.pin_dc = LOVYAN_DC; 42 | 43 | bus->config(cfg); 44 | panel->setBus(bus); 45 | } 46 | 47 | { 48 | auto cfg = panel->config(); 49 | 50 | cfg.pin_cs = LOVYAN_CS; 51 | cfg.pin_rst = LOVYAN_RST; 52 | cfg.pin_busy = -1; 53 | 54 | 55 | cfg.memory_width = LOVYAN_WIDTH; 56 | cfg.memory_height = LOVYAN_HEIGHT; 57 | cfg.panel_width = LOVYAN_WIDTH; 58 | cfg.panel_height = LOVYAN_HEIGHT; 59 | cfg.offset_x = 0; 60 | cfg.offset_y = 0; 61 | cfg.offset_rotation = 0; 62 | cfg.dummy_read_pixel = 8; 63 | cfg.dummy_read_bits = 1; 64 | cfg.readable = true; 65 | cfg.invert = false; 66 | cfg.rgb_order = false; 67 | cfg.dlen_16bit = false; 68 | cfg.bus_shared = true; 69 | 70 | #ifdef LOVYAN_BGR 71 | cfg.rgb_order = true; 72 | #endif 73 | 74 | panel->config(cfg); 75 | } 76 | 77 | setPanel(panel); 78 | #endif 79 | } 80 | }; 81 | } 82 | 83 | #include 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/Elements/GridMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_GRIDMENU_H 2 | #define CIRCUITOS_GRIDMENU_H 3 | 4 | 5 | #include "../UI/Layout.h" 6 | #include "../UI/Image.h" 7 | #include "../UI/GridLayout.h" 8 | #include "../UI/LinearLayout.h" 9 | #include "../UI/ScrollLayout.h" 10 | #include "../Util/Vector.h" 11 | 12 | struct GridMenuItem { 13 | const char* title; 14 | Element* icon; 15 | }; 16 | 17 | class GridMenu : public LinearLayout { 18 | public: 19 | GridMenu(ElementContainer* parent, uint cols); 20 | virtual ~GridMenu(); 21 | 22 | void reflow() override; 23 | 24 | void addItem(const GridMenuItem& item); 25 | 26 | /** 27 | * Relocate a menu item. See Vector::relocate 28 | * @see Vector::relocate 29 | * @param oldPos 30 | */ 31 | void relocate(uint oldPos, uint newPos); 32 | 33 | /** 34 | * Selects an element in the menu. Clears and redraws parent. 35 | * @param element 36 | */ 37 | void selectElement(uint element); 38 | void selectNext(); 39 | void selectPrev(); 40 | 41 | /** 42 | * Sets the selected element. Does not perform any drawing. 43 | * @param element 44 | * @return True if a redraw is needed due to scrolling. 45 | */ 46 | bool setSelected(uint element); 47 | uint getSelected() const; 48 | GridMenuItem& getSelectedItem(); 49 | void clearItems(); 50 | 51 | Color getSelectedColor() const; 52 | void setSelectedColor(Color selectedColor); 53 | void setTitleColor(Color titleBgColor, Color titleFgColor); 54 | 55 | void draw() override; 56 | 57 | private: 58 | Image* title; 59 | ScrollLayout* scroller; 60 | GridLayout* grid; 61 | 62 | Vector items; 63 | Color selectedColor = TFT_RED; 64 | Color titleBgColor = TFT_DARKGREEN; 65 | Color titleFgColor = TFT_BLACK; 66 | 67 | uint selected = 0; 68 | bool scroll(); 69 | void drawTitle(); 70 | 71 | }; 72 | 73 | 74 | #endif //CIRCUITOS_GRIDMENU_H 75 | -------------------------------------------------------------------------------- /src/Elements/ListMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_LISTMENU_H 2 | #define CIRCUITOS_LISTMENU_H 3 | 4 | 5 | #include "../UI/LinearLayout.h" 6 | #include "../UI/ScrollLayout.h" 7 | #include "../UI/Image.h" 8 | 9 | struct ListMenuItem { 10 | const char* title; 11 | Image* image; 12 | }; 13 | 14 | class ListMenu : public LinearLayout { 15 | public: 16 | ListMenu(ElementContainer* parent, String title); 17 | virtual ~ListMenu(); 18 | 19 | void reflow() override; 20 | 21 | void addItem(const char* title); 22 | 23 | /** 24 | * Relocate a menu item. See Vector::relocate 25 | * @see Vector::relocate 26 | * @param oldPos 27 | */ 28 | void relocate(uint oldPos, uint newPos); 29 | 30 | /** 31 | * Selects an element in the menu. Clears and redraws parent. 32 | * @param element 33 | */ 34 | void selectElement(uint element); 35 | void selectNext(); 36 | void selectPrev(); 37 | 38 | /** 39 | * Sets the selected element. Does not perform any drawing. 40 | * @param element 41 | * @return True if a redraw is needed due to scrolling. 42 | */ 43 | bool setSelected(uint element); 44 | uint getSelected() const; 45 | ListMenuItem& getSelectedItem(); 46 | void clearItems(); 47 | 48 | Color getSelectedColor() const; 49 | void setSelectedBgColor(Color selectedBgColor); 50 | void setItemColor(Color titleBgColor, Color titleFgColor); 51 | void setTitleColor(Color titleBgColor, Color titleFgColor); 52 | 53 | void draw() override; 54 | 55 | private: 56 | Image* titleImage; 57 | ScrollLayout* scroller; 58 | LinearLayout* list; 59 | 60 | Vector items; 61 | Color selectedBgColor = TFT_LIGHTGREY; 62 | Color itemBgColor = TFT_DARKGREY; 63 | Color itemFgColor = TFT_BLACK; 64 | Color titleBgColor = TFT_DARKGREEN; 65 | Color titleFgColor = TFT_BLACK; 66 | 67 | String title = ""; 68 | 69 | uint selected = 0; 70 | bool scroll(); 71 | void drawTitle(); 72 | void drawItems(); 73 | void drawItem(uint index); 74 | 75 | }; 76 | 77 | 78 | #endif //CIRCUITOS_LISTMENU_H 79 | -------------------------------------------------------------------------------- /src/Elements/ModalBackground.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MODALBG_H 2 | #define CIRCUITOS_MODALBG_H 3 | 4 | #include "../UI/CustomElement.h" 5 | 6 | class ModalBackground : public CustomElement { 7 | public: 8 | ModalBackground(ElementContainer *parent, uint width, uint height, Color bgColor, Color topColor, Color botColor) : 9 | CustomElement(parent, width, height), bgColor(bgColor), borderTopColor(topColor), borderBotColor(botColor){ 10 | getSprite()->setChroma(TFT_TRANSPARENT); 11 | } 12 | 13 | ModalBackground(ElementContainer *parent, uint width, uint height, Color _bgColor) : 14 | ModalBackground(parent, width, height, _bgColor, (_bgColor & 0x7BEF) << 1,(_bgColor & 0xF7DE) >> 1){ 15 | } 16 | 17 | void draw() override{ 18 | Sprite *canvas = getSprite(); 19 | 20 | canvas->clear(TFT_TRANSPARENT); 21 | canvas->fillRect(getTotalX(), getTotalY() + 2, getWidth(), getHeight() - 4, bgColor); 22 | canvas->drawFastHLine(getTotalX() + 1, getTotalY(), getWidth() - 2, borderTopColor); 23 | canvas->drawFastHLine(getTotalX(), getTotalY() + 1, getWidth(), borderTopColor); 24 | canvas->drawFastHLine(getTotalX(), getTotalY() + getHeight() - 2, getWidth(), borderBotColor); 25 | canvas->drawFastHLine(getTotalX() + 1, getTotalY() + getHeight() - 1, getWidth() - 2, borderBotColor); 26 | 27 | if(border){ 28 | canvas->drawFastVLine(getTotalX() + 0, getTotalY() + 2, getHeight() - 4, borderTopColor); 29 | canvas->drawFastVLine(getTotalX() + 1, getTotalY() + 2, getHeight() - 4, borderTopColor); 30 | canvas->drawFastVLine(getTotalX() + getWidth() - 2, getTotalY() + 2, getHeight() - 4, borderTopColor); 31 | canvas->drawFastVLine(getTotalX() + getWidth() - 1, getTotalY() + 2, getHeight() - 4, borderTopColor); 32 | } 33 | } 34 | 35 | bool border = false; 36 | 37 | const Color bgColor; 38 | Color borderTopColor; 39 | Color borderBotColor; 40 | 41 | }; 42 | 43 | #endif //CIRCUITOS_MODALBG_H 44 | -------------------------------------------------------------------------------- /src/Elements/SliderElement.cpp: -------------------------------------------------------------------------------- 1 | #include "SliderElement.h" 2 | 3 | SliderElement::SliderElement(ElementContainer* parent, uint width, uint height) : 4 | Layout(parent){ 5 | 6 | image = new Image(this, width, height); 7 | children.push_back(image); 8 | } 9 | 10 | SliderElement::~SliderElement(){ 11 | delete image; 12 | } 13 | 14 | void SliderElement::reposChildren(){ 15 | uint newPos = getAvailableWidth() - image->getWidth() - getPadding() - progress / speed; 16 | children[0]->setPos(newPos, 0); 17 | } 18 | 19 | void SliderElement::loop(uint millis){ 20 | if(started){ 21 | progress += millis; 22 | reposChildren(); 23 | } 24 | 25 | if(progress / speed > getParent()->getAvailableWidth() - image->getWidth() - 10){ 26 | started = false; 27 | 28 | if(completeListener){ 29 | progress = 0; 30 | reposChildren(); 31 | 32 | completeListener(); 33 | } 34 | } 35 | } 36 | 37 | Sprite* SliderElement::getImageSprite(){ 38 | return image->getSprite(); 39 | } 40 | 41 | void SliderElement::start(){ 42 | started = true; 43 | 44 | if(startListener) startListener(); 45 | } 46 | 47 | void SliderElement::stop(){ 48 | started = false; 49 | uint distance = progress / speed; 50 | progress = 0; 51 | reposChildren(); 52 | 53 | // TODO: just under full 54 | if(distance <= 40){ 55 | if(shortListener) shortListener(); 56 | }else if(distance < getParent()->getAvailableWidth() - image->getWidth() - 10){ 57 | if(longListener) longListener(); 58 | } 59 | } 60 | 61 | void SliderElement::setStartListener(void (* listener)()){ 62 | SliderElement::startListener = listener; 63 | } 64 | 65 | void SliderElement::setStopListener(void (* listener)()){ 66 | SliderElement::startListener = listener; 67 | } 68 | 69 | void SliderElement::setShortListener(void (* listener)()){ 70 | SliderElement::shortListener = listener; 71 | } 72 | 73 | void SliderElement::setLongListener(void (* listener)()){ 74 | SliderElement::longListener = listener; 75 | } 76 | 77 | void SliderElement::setCompleteListener(void (* listener)()){ 78 | SliderElement::completeListener = listener; 79 | } 80 | 81 | void SliderElement::setSpeed(uint speed){ 82 | SliderElement::speed = speed; 83 | } 84 | -------------------------------------------------------------------------------- /src/Elements/SliderElement.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SLIDERELEMENT_H 2 | #define CIRCUITOS_SLIDERELEMENT_H 3 | 4 | #include "../UI/SpriteElement.h" 5 | #include "../UI/Layout.h" 6 | #include "../UI/Image.h" 7 | #include "../Loop/LoopListener.h" 8 | 9 | class SliderElement : public Layout, public LoopListener { 10 | public: 11 | SliderElement(ElementContainer* parent, uint width, uint height); 12 | ~SliderElement(); 13 | 14 | void loop(uint millis) override; 15 | 16 | void start(); 17 | void stop(); 18 | 19 | Sprite* getImageSprite(); 20 | 21 | void setStartListener(void (* listener)()); 22 | void setStopListener(void (* listener)()); 23 | void setShortListener(void (* listener)()); 24 | void setLongListener(void (* listener)()); 25 | void setCompleteListener(void (* listener)()); 26 | 27 | void setSpeed(uint speed); 28 | 29 | private: 30 | Image* image; 31 | 32 | uint started = false; 33 | uint progress = 0; 34 | uint speed = 5; 35 | 36 | void (*startListener)() = nullptr; 37 | void (*stopListener)() = nullptr; 38 | void (*shortListener)() = nullptr; 39 | void (*longListener)() = nullptr; 40 | void (*completeListener)() = nullptr; 41 | 42 | void reposChildren() override; 43 | 44 | }; 45 | 46 | 47 | #endif //CIRCUITOS_SLIDERELEMENT_H 48 | -------------------------------------------------------------------------------- /src/FS/CompressedFile.cpp: -------------------------------------------------------------------------------- 1 | #include "CompressedFile.h" 2 | 3 | CompressedFile::CompressedFile(fs::File f, uint8_t expansionBits, uint8_t lookaheadBits, size_t readBufferSize) : f(f), fileBuffer(f, readBufferSize){ 4 | decoder = heatshrink_decoder_alloc(readBufferSize, expansionBits, lookaheadBits); 5 | } 6 | 7 | CompressedFile::~CompressedFile(){ 8 | heatshrink_decoder_free(decoder); 9 | } 10 | 11 | fs::File CompressedFile::open(fs::File f, uint8_t expansionBits, uint8_t lookaheadBits, size_t readBufferSize){ 12 | return File(std::make_shared(f, expansionBits, lookaheadBits, readBufferSize)); 13 | } 14 | 15 | size_t CompressedFile::write(const uint8_t* buf, size_t size){ 16 | return 0; 17 | } 18 | 19 | size_t CompressedFile::read(uint8_t* buf, size_t size){ 20 | size_t bytesWritten = 0; 21 | fileBuffer.refill(); 22 | 23 | while(bytesWritten < size){ 24 | if(fileBuffer.available() > 0){ 25 | size_t read = 0; 26 | heatshrink_decoder_sink(decoder, fileBuffer.data(), fileBuffer.available(), &read); 27 | fileBuffer.moveRead(read); 28 | }else{ 29 | heatshrink_decoder_finish(decoder); 30 | } 31 | 32 | size_t bytesDecoded = 0; 33 | HSD_poll_res res = heatshrink_decoder_poll(decoder, buf + bytesWritten, size - bytesWritten, &bytesDecoded); 34 | bytesWritten += bytesDecoded; 35 | 36 | fileBuffer.refill(); 37 | if(res == HSDR_POLL_EMPTY && fileBuffer.available() == 0){ 38 | break; 39 | } 40 | } 41 | 42 | pos += bytesWritten; 43 | return bytesWritten; 44 | } 45 | 46 | void CompressedFile::flush(){ 47 | } 48 | 49 | bool CompressedFile::seek(uint32_t pos, fs::SeekMode mode){ 50 | if(pos == 0 && mode == SeekSet){ 51 | this->pos = 0; 52 | f.seek(0); 53 | fileBuffer.clear(); 54 | heatshrink_decoder_reset(decoder); 55 | } 56 | } 57 | 58 | size_t CompressedFile::position() const{ 59 | return pos; 60 | } 61 | 62 | size_t CompressedFile::size() const{ 63 | return 0; 64 | } 65 | 66 | void CompressedFile::close(){ 67 | f.close(); 68 | } 69 | 70 | time_t CompressedFile::getLastWrite(){ 71 | return f.getLastWrite(); 72 | } 73 | 74 | const char* CompressedFile::name() const{ 75 | return f.name(); 76 | } 77 | 78 | boolean CompressedFile::isDirectory(void){ 79 | return f.isDirectory(); 80 | } 81 | 82 | fs::FileImplPtr CompressedFile::openNextFile(const char* mode){ 83 | return fs::FileImplPtr(); 84 | } 85 | 86 | void CompressedFile::rewindDirectory(void){ 87 | f.rewindDirectory(); 88 | } 89 | 90 | CompressedFile::operator bool(){ 91 | return (bool)(f); 92 | } 93 | 94 | bool CompressedFile::truncate(uint32_t size){ 95 | return false; 96 | } 97 | 98 | const char* CompressedFile::fullName() const{ 99 | #ifdef ESP8266 100 | return f.fullName(); 101 | #else 102 | return f.name(); 103 | #endif 104 | } 105 | 106 | bool CompressedFile::isFile() const{ 107 | return true; 108 | } 109 | 110 | bool CompressedFile::isDirectory() const{ 111 | return false; 112 | } 113 | -------------------------------------------------------------------------------- /src/FS/CompressedFile.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_COMPRESSEDFILE_H 2 | #define CIRCUITOS_COMPRESSEDFILE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../Buffer/FSBuffer.h" 9 | #include "../Buffer/DataBuffer.h" 10 | 11 | #define FSBUFFER_SIZE 512 12 | 13 | class CompressedFile : public fs::FileImpl { 14 | public: 15 | #ifdef ESP32 16 | boolean isDirectory(void) override; 17 | fs::FileImplPtr openNextFile(const char* mode) override; 18 | void rewindDirectory(void) override; 19 | operator bool() override; 20 | #else 21 | boolean isDirectory(void) ; 22 | fs::FileImplPtr openNextFile(const char* mode) ; 23 | void rewindDirectory(void) ; 24 | operator bool() ; 25 | #endif 26 | CompressedFile(fs::File f, uint8_t expansionBits, uint8_t lookaheadBits, size_t readBufferSize = FSBUFFER_SIZE); 27 | ~CompressedFile(); 28 | static fs::File open(fs::File f, uint8_t expansionBits, uint8_t lookaheadBits, size_t readBufferSize = FSBUFFER_SIZE); 29 | 30 | size_t write(const uint8_t* buf, size_t size) override; 31 | size_t read(uint8_t* buf, size_t size) override; 32 | void flush() override; 33 | bool seek(uint32_t pos, fs::SeekMode mode = fs::SeekMode::SeekSet) override; 34 | size_t position() const override; 35 | size_t size() const override; 36 | void close() override; 37 | time_t getLastWrite() override; 38 | const char* name() const override; 39 | 40 | bool truncate(uint32_t size) ; 41 | 42 | const char* fullName() const ; 43 | 44 | bool isFile() const ; 45 | 46 | bool isDirectory() const ; 47 | 48 | private: 49 | fs::File f; 50 | heatshrink_decoder *decoder; 51 | FSBuffer fileBuffer; 52 | size_t pos = 0; 53 | }; 54 | 55 | 56 | #endif //CIRCUITOS_COMPRESSEDFILE_H 57 | -------------------------------------------------------------------------------- /src/FS/PGMFile.cpp: -------------------------------------------------------------------------------- 1 | #include "PGMFile.h" 2 | 3 | PGMFile::PGMFile(const uint8_t* data, size_t size) : data(data), dataSize(size){ 4 | 5 | } 6 | 7 | PGMFile::~PGMFile(){ 8 | close(); 9 | } 10 | 11 | fs::File PGMFile::open(const uint8_t* data, size_t size){ 12 | return File(std::make_shared(data, size)); 13 | } 14 | 15 | size_t PGMFile::write(uint8_t data){ 16 | return 0; 17 | // this->data[cursor++] = data; 18 | return 1; 19 | } 20 | 21 | size_t PGMFile::write(const uint8_t* buf, size_t size){ 22 | return 0; 23 | //memcpy(data + cursor, buf, size); 24 | cursor += size; 25 | } 26 | 27 | int PGMFile::available(){ 28 | return cursor < dataSize; 29 | } 30 | 31 | int PGMFile::read(){ 32 | return pgm_read_byte(data + cursor++); 33 | } 34 | 35 | int PGMFile::peek(){ 36 | return pgm_read_byte(data + cursor); 37 | } 38 | 39 | void PGMFile::flush(){ 40 | 41 | } 42 | 43 | bool PGMFile::seek(uint32_t pos, fs::SeekMode mode){ 44 | switch(mode){ 45 | case fs::SeekSet: 46 | cursor = pos; 47 | break; 48 | case fs::SeekEnd: 49 | cursor = dataSize - pos - 1; 50 | break; 51 | case fs::SeekCur: 52 | cursor += pos; 53 | break; 54 | } 55 | } 56 | 57 | bool PGMFile::seek(uint32_t pos){ 58 | return seek(pos, SeekSet); 59 | } 60 | 61 | size_t PGMFile::read(uint8_t* buf, size_t size){ 62 | if(cursor >= dataSize) return 0; 63 | 64 | size_t availableSize; 65 | if(cursor + size <= dataSize){ 66 | availableSize = size; 67 | }else{ 68 | availableSize = dataSize - cursor - 1; 69 | } 70 | 71 | for(int i = 0; i < availableSize; i++){ 72 | buf[i] = read(); 73 | } 74 | 75 | return availableSize; 76 | } 77 | 78 | size_t PGMFile::position() const{ 79 | return cursor; 80 | } 81 | 82 | size_t PGMFile::size() const{ 83 | return dataSize; 84 | } 85 | 86 | void PGMFile::close(){ 87 | data = nullptr; 88 | dataSize = 0; 89 | } 90 | 91 | PGMFile::operator bool(){ 92 | return data != nullptr; 93 | } 94 | 95 | bool PGMFile::isDirectory(){ 96 | return false; 97 | } 98 | 99 | time_t PGMFile::getLastWrite(){ 100 | return 0; 101 | } 102 | 103 | const char* PGMFile::name() const { 104 | return nullptr; 105 | } 106 | 107 | void PGMFile::rewindDirectory() { 108 | 109 | } 110 | 111 | fs::FileImplPtr PGMFile::openNextFile(const char* mode){ 112 | return fs::FileImplPtr(); 113 | } 114 | 115 | bool PGMFile::truncate(uint32_t size){ 116 | return false; 117 | } 118 | 119 | const char* PGMFile::fullName() const{ 120 | return nullptr; 121 | } 122 | 123 | bool PGMFile::isFile() const{ 124 | return false; 125 | } 126 | 127 | bool PGMFile::isDirectory() const{ 128 | return false; 129 | } 130 | -------------------------------------------------------------------------------- /src/FS/PGMFile.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_PGMFILE_H 2 | #define CIRCUITOS_PGMFILE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class PGMFile : public fs::FileImpl { 9 | public: 10 | #ifdef ESP32 11 | fs::FileImplPtr openNextFile(const char* mode) override; 12 | void rewindDirectory() override; 13 | operator bool() override; 14 | #else 15 | fs::FileImplPtr openNextFile(const char* mode) ; 16 | void rewindDirectory() ; 17 | operator bool() ; 18 | #endif 19 | PGMFile(const uint8_t* data, size_t size); 20 | virtual ~PGMFile(); 21 | 22 | static fs::File open(const uint8_t* data, size_t size); 23 | 24 | size_t write(const uint8_t* buf, size_t size) override; 25 | size_t read(uint8_t* buf, size_t size) override; 26 | void flush() override; 27 | bool seek(uint32_t pos, fs::SeekMode mode) override; 28 | size_t position() const override; 29 | size_t size() const override; 30 | void close() override; 31 | time_t getLastWrite() override; 32 | const char* name() const override; 33 | bool isDirectory(); 34 | 35 | bool truncate(uint32_t size) ; 36 | 37 | const char* fullName() const ; 38 | 39 | bool isFile() const ; 40 | 41 | bool isDirectory() const ; 42 | 43 | bool seek(uint32_t pos); 44 | size_t write(uint8_t data); 45 | int read(); 46 | int peek(); 47 | int available(); 48 | 49 | private: 50 | const uint8_t* data; 51 | size_t dataSize; 52 | size_t cursor = 0; 53 | }; 54 | 55 | 56 | #endif //CIRCUITOS_PGMFILE_H 57 | -------------------------------------------------------------------------------- /src/FS/RamFile.cpp: -------------------------------------------------------------------------------- 1 | #include "RamFile.h" 2 | 3 | RamFile::RamFile(uint8_t* data, size_t size, bool readonly) : data(data), dataSize(size), readonly(readonly){ 4 | 5 | } 6 | 7 | RamFile::~RamFile(){ 8 | RamFile::close(); 9 | } 10 | 11 | fs::File RamFile::open(uint8_t* data, size_t size, bool readonly){ 12 | return File(std::make_shared(data, size, readonly)); 13 | } 14 | 15 | fs::File RamFile::open(fs::File file, bool readonly){ 16 | uint8_t* data; 17 | #ifdef CONFIG_SPIRAM_SUPPORT 18 | data = static_cast(ps_malloc(file.size())); 19 | #else 20 | data = static_cast(malloc(file.size())); 21 | #endif 22 | 23 | file.seek(0); 24 | file.readBytes(reinterpret_cast(data), file.size()); 25 | 26 | auto f = std::make_shared(data, file.size(), readonly); 27 | 28 | f->filename = file.name(); 29 | 30 | return File(f); 31 | } 32 | 33 | fs::File RamFile::create(const String& filename){ 34 | auto f = std::make_shared(nullptr, 0, false); 35 | f->filename = filename; 36 | return File(f); 37 | } 38 | 39 | size_t RamFile::write(uint8_t data){ 40 | if(readonly) return 0; 41 | 42 | if(cursor == dataSize){ 43 | dataSize++; 44 | } 45 | 46 | this->data[cursor++] = data; 47 | return 1; 48 | } 49 | 50 | size_t RamFile::write(const uint8_t* buf, size_t size){ 51 | if(readonly) return 0; 52 | if(size == 0) return 0; 53 | 54 | if(cursor + size > dataSize){ 55 | dataSize = size + cursor; 56 | #ifdef CONFIG_SPIRAM_SUPPORT 57 | data = static_cast(ps_realloc(data, dataSize)); 58 | #else 59 | data = static_cast(realloc(data, dataSize)); 60 | #endif 61 | } 62 | 63 | memcpy(data + cursor, buf, size); 64 | cursor += size; 65 | 66 | return size; 67 | } 68 | 69 | int RamFile::available(){ 70 | return dataSize - cursor; 71 | } 72 | 73 | int RamFile::read(){ 74 | return *(data + cursor++); 75 | } 76 | 77 | int RamFile::peek(){ 78 | return *(data + cursor); 79 | } 80 | 81 | void RamFile::flush(){ 82 | 83 | } 84 | 85 | bool RamFile::seek(uint32_t pos, fs::SeekMode mode){ 86 | if(mode == fs::SeekSet){ 87 | cursor = pos; 88 | }else if(mode == fs::SeekEnd){ 89 | cursor = dataSize - pos; 90 | }else if(mode == fs::SeekCur){ 91 | cursor += pos; 92 | } 93 | } 94 | 95 | bool RamFile::seek(uint32_t pos){ 96 | return seek(pos, SeekSet); 97 | } 98 | 99 | size_t RamFile::read(uint8_t* buf, size_t size){ 100 | if(cursor >= dataSize) return 0; 101 | 102 | size_t availableSize; 103 | if(cursor + size <= dataSize){ 104 | availableSize = size; 105 | }else{ 106 | availableSize = dataSize - cursor - 1; 107 | } 108 | 109 | for(int i = 0; i < availableSize; i++){ 110 | buf[i] = read(); 111 | } 112 | 113 | return availableSize; 114 | } 115 | 116 | size_t RamFile::position() const{ 117 | return cursor; 118 | } 119 | 120 | size_t RamFile::size() const{ 121 | return dataSize; 122 | } 123 | 124 | void RamFile::close(){ 125 | delete data; 126 | 127 | data = nullptr; 128 | dataSize = 0; 129 | filename = ""; 130 | } 131 | 132 | RamFile::operator bool(){ 133 | return data != nullptr; 134 | } 135 | 136 | bool RamFile::isDirectory(){ 137 | return false; 138 | } 139 | 140 | time_t RamFile::getLastWrite(){ 141 | return 0; 142 | } 143 | 144 | const char* RamFile::name() const { 145 | return filename.c_str(); 146 | } 147 | 148 | void RamFile::rewindDirectory() { 149 | 150 | } 151 | 152 | fs::FileImplPtr RamFile::openNextFile(const char* mode){ 153 | return fs::FileImplPtr(); 154 | } 155 | 156 | bool RamFile::truncate(uint32_t size){ 157 | return false; 158 | } 159 | 160 | const char* RamFile::fullName() const{ 161 | return nullptr; 162 | } 163 | 164 | bool RamFile::isFile() const{ 165 | return false; 166 | } 167 | 168 | bool RamFile::isDirectory() const{ 169 | return false; 170 | } 171 | -------------------------------------------------------------------------------- /src/FS/RamFile.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_RAMFILE_H 2 | #define CIRCUITOS_RAMFILE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class RamFile : public fs::FileImpl { 9 | public: 10 | #ifdef ESP32 11 | fs::FileImplPtr openNextFile(const char* mode) override; 12 | void rewindDirectory() override; 13 | operator bool() override; 14 | #else 15 | fs::FileImplPtr openNextFile(const char* mode) ; 16 | void rewindDirectory() ; 17 | operator bool() ; 18 | #endif 19 | RamFile(uint8_t* data, size_t size, bool readonly = true); 20 | virtual ~RamFile(); 21 | 22 | static fs::File open(uint8_t* data, size_t size, bool readonly = true); 23 | static fs::File open(fs::File file, bool readonly = true); 24 | static fs::File create(const String& filename = ""); 25 | 26 | size_t write(const uint8_t* buf, size_t size) override; 27 | size_t read(uint8_t* buf, size_t size) override; 28 | void flush() override; 29 | bool seek(uint32_t pos, fs::SeekMode mode) override; 30 | size_t position() const override; 31 | size_t size() const override; 32 | void close() override; 33 | time_t getLastWrite() override; 34 | const char* name() const override; 35 | bool isDirectory(); 36 | 37 | bool truncate(uint32_t size) ; 38 | 39 | const char* fullName() const ; 40 | 41 | bool isFile() const ; 42 | 43 | bool isDirectory() const ; 44 | 45 | bool seek(uint32_t pos); 46 | size_t write(uint8_t data); 47 | int read(); 48 | int peek(); 49 | int available(); 50 | 51 | private: 52 | uint8_t* data = nullptr; 53 | size_t dataSize; 54 | size_t cursor = 0; 55 | bool readonly; 56 | String filename; 57 | 58 | }; 59 | 60 | 61 | #endif //CIRCUITOS_RAMFILE_H 62 | -------------------------------------------------------------------------------- /src/Input/I2cExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "I2cExpander.h" 2 | 3 | #define INPUT_REG 0x00 // input register location 4 | #define OUTPUT_REG 0x02 // output register location 5 | #define INVERT_REG 0x04 // polarity inversion register location 6 | #define CONFIG_REG 0x06 // configuration register location 7 | 8 | I2cExpander* I2cExpander::instance = nullptr; 9 | 10 | I2cExpander::I2cExpander() 11 | { 12 | instance = this; 13 | } 14 | I2cExpander::~I2cExpander() 15 | { 16 | } 17 | bool I2cExpander::begin(uint8_t _address, uint8_t _sda, uint8_t _scl) 18 | { 19 | address = _address; 20 | Wire.begin(_sda, _scl); 21 | 22 | Wire.beginTransmission(address); 23 | if(Wire.endTransmission() != 0){ 24 | return false; 25 | } 26 | 27 | portWrite(0xFFFF); 28 | _write(0x0000, INVERT_REG); 29 | portConfig(0xFFFF); 30 | 31 | return true; 32 | } 33 | void I2cExpander::_write(uint16_t _portData, uint8_t reg) 34 | { 35 | Wire.beginTransmission(address); 36 | Wire.write(reg); 37 | // Wire.write(_portData); 38 | Wire.write(_portData & 0x00FF); 39 | Wire.write(_portData >> 8); 40 | Wire.endTransmission(); 41 | } 42 | uint16_t I2cExpander::portRead() 43 | { 44 | Wire.beginTransmission(address); 45 | Wire.write(INPUT_REG); 46 | Wire.endTransmission(); 47 | Wire.requestFrom((uint8_t) address, (uint8_t) 2); 48 | uint16_t readValue = Wire.read(); 49 | readValue |= Wire.read() << 8; 50 | // Wire.readBytes((uint8_t*)readValue, 2); 51 | portState = readValue; 52 | return readValue; 53 | } 54 | 55 | bool I2cExpander::portRead(uint16_t& state){ 56 | Wire.beginTransmission(address); 57 | Wire.write(INPUT_REG); 58 | if(Wire.endTransmission() != 0) return false; 59 | 60 | Wire.requestFrom((uint8_t) address, (uint8_t) 2); 61 | if(Wire.readBytes(reinterpret_cast(&state), 2) != 2){ 62 | state = 0; 63 | return false; 64 | } 65 | portState = state; 66 | return true; 67 | 68 | } 69 | 70 | void I2cExpander::portConfig(uint16_t _portData) 71 | { 72 | configState = _portData; 73 | _write(configState, CONFIG_REG); 74 | } 75 | void I2cExpander::portWrite(uint16_t writeData) 76 | { 77 | outputState = writeData; 78 | _write(writeData, OUTPUT_REG); 79 | } 80 | void I2cExpander::pinMode(uint8_t pin, uint8_t mode) 81 | { 82 | uint16_t configMask = 1 << pin; 83 | if(mode == OUTPUT) 84 | configState &= ~configMask; 85 | else 86 | configState |= configMask; 87 | 88 | portConfig(configState); 89 | } 90 | bool I2cExpander::pinRead(uint8_t pin) 91 | { 92 | uint16_t _data = portRead(); 93 | return bitRead(_data, pin); 94 | } 95 | void I2cExpander::pinWrite(uint8_t pin, bool state) 96 | { 97 | uint16_t outputMask = 1 << pin; 98 | if(state) 99 | outputState |= outputMask; 100 | else 101 | outputState &= ~outputMask; 102 | portWrite(outputState); 103 | } 104 | I2cExpander* I2cExpander::getInstance() 105 | { 106 | return instance; 107 | } 108 | 109 | uint16_t I2cExpander::getPortState() const{ 110 | return portState; 111 | } 112 | -------------------------------------------------------------------------------- /src/Input/I2cExpander.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef CIRCUITOS_I2C_EXPANDER_H 5 | #define CIRCUITOS_I2C_EXPANDER_H 6 | /* 7 | Made for Texas Instruments 16-bit GPIO expanders 8 | (e.g. PCF9539) 9 | */ 10 | 11 | class I2cExpander 12 | { 13 | public: 14 | I2cExpander(); 15 | ~I2cExpander(); 16 | bool begin(uint8_t _address = 0x74, uint8_t _sda = 27, uint8_t _scl = 14); 17 | uint16_t portRead(); 18 | bool portRead(uint16_t& state); 19 | void pinMode(uint8_t pin, uint8_t mode); 20 | bool pinRead(uint8_t pin); 21 | void pinWrite(uint8_t pin, bool state); 22 | void portWrite(uint16_t _portData); 23 | void portConfig(uint16_t _portData); 24 | uint16_t getPortState() const; 25 | 26 | static I2cExpander* getInstance(); 27 | 28 | private: 29 | void _write(uint16_t port, uint8_t reg); 30 | uint8_t address = 0; 31 | uint16_t configState = 0; 32 | uint16_t outputState = 0; 33 | uint16_t portState = 0; 34 | static I2cExpander* instance; 35 | }; 36 | 37 | #endif -------------------------------------------------------------------------------- /src/Input/InputGPIO.cpp: -------------------------------------------------------------------------------- 1 | #include "InputGPIO.h" 2 | #include "../Util/Debug.h" 3 | 4 | InputGPIO::InputGPIO() : Input(PIN_MAX){ 5 | 6 | } 7 | 8 | void InputGPIO::scanButtons(){ 9 | for(uint i = 0; i < buttons.size(); i++){ 10 | if(digitalRead(buttons[i])){ 11 | //released 12 | Input::btnRelease(i); 13 | }else{ 14 | Input::btnPress(i); 15 | } 16 | } 17 | } 18 | 19 | void InputGPIO::registerButton(uint8_t pin){ 20 | pinMode(pin, INPUT_PULLUP); 21 | Input::registerButton(pin); 22 | } -------------------------------------------------------------------------------- /src/Input/InputGPIO.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_INPUTGPIO_H 2 | #define CIRCUITOS_INPUTGPIO_H 3 | 4 | #include "Input.h" 5 | #include "../Util/Vector.h" 6 | 7 | #define PIN_MAX 45 8 | #define DEBOUNCE_COUNT 1 9 | 10 | class InputGPIO : public Input { 11 | public: 12 | InputGPIO(); 13 | 14 | private: 15 | void registerButton(uint8_t pin) override; 16 | void scanButtons() override; 17 | }; 18 | 19 | 20 | #endif //CIRCUITOS_INPUTGPIO_H 21 | -------------------------------------------------------------------------------- /src/Input/InputI2C.cpp: -------------------------------------------------------------------------------- 1 | #include "InputI2C.h" 2 | #include "I2cExpander.h" 3 | 4 | InputI2C::InputI2C(I2cExpander* _i2c) : Input(I2C_PIN_MAX) , i2c(_i2c){ 5 | 6 | } 7 | 8 | void InputI2C::scanButtons(){ 9 | uint16_t portScan; 10 | if(!i2c->portRead(portScan)) return; 11 | 12 | for(uint i = 0; i < buttons.size(); i++){ 13 | if(bitRead(portScan, buttons[i])){ 14 | Input::btnRelease(i); 15 | } 16 | else{ 17 | Input::btnPress(i); 18 | } 19 | 20 | } 21 | } 22 | 23 | void InputI2C::registerButton(uint8_t pin){ 24 | i2c->pinMode(pin, INPUT_PULLUP); 25 | Input::registerButton(pin); 26 | } -------------------------------------------------------------------------------- /src/Input/InputI2C.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_INPUTI2C_H 2 | #define CIRCUITOS_INPUTI2C_H 3 | 4 | #include "Input.h" 5 | 6 | #define I2C_PIN_MAX 16 7 | #define DEBOUNCE_COUNT 1 8 | 9 | class I2cExpander; 10 | 11 | class InputI2C : public Input { 12 | public: 13 | InputI2C(I2cExpander* _i2c); 14 | 15 | private: 16 | I2cExpander* i2c; 17 | void registerButton(uint8_t pin) override; 18 | void scanButtons() override; 19 | }; 20 | 21 | 22 | #endif //CIRCUITOS_INPUTI2C_H 23 | -------------------------------------------------------------------------------- /src/Input/InputListener.cpp: -------------------------------------------------------------------------------- 1 | #include "InputListener.h" 2 | 3 | void InputListener::buttonPressed(uint i) {} 4 | void InputListener::buttonReleased(uint i) {} 5 | void InputListener::buttonHeld(uint i) {} 6 | void InputListener::buttonHeldRepeat(uint i, uint repeatCount) {} 7 | void InputListener::anyKeyPressed() {} 8 | 9 | void InputListener::setButtonHoldAndRepeatTime(uint8_t button, uint32_t holdAndRepeatTime){ 10 | holdAndRepeatTimes.insert({ button, {holdAndRepeatTime, 0} }); 11 | } 12 | 13 | void InputListener::setButtonHoldTime(uint8_t button, uint32_t holdTime){ 14 | holdTimes.insert({button, {holdTime, false}}); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/Input/InputListener.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_INPUTLISTENER_H 2 | #define CIRCUITOS_INPUTLISTENER_H 3 | 4 | #include 5 | #include 6 | 7 | class Input; 8 | 9 | struct HoldData { 10 | uint32_t time; 11 | bool holdingOver; 12 | }; 13 | struct HoldRepeatData { 14 | uint32_t time; 15 | uint32_t repeatCounter; 16 | }; 17 | 18 | class InputListener { 19 | friend Input; 20 | 21 | protected: 22 | void setButtonHoldTime(uint8_t button, uint32_t holdTime); 23 | void setButtonHoldAndRepeatTime(uint8_t button, uint32_t holdAndRepeatTime); 24 | 25 | private: 26 | virtual void buttonPressed(uint i); 27 | virtual void buttonReleased(uint i); 28 | virtual void buttonHeld(uint i); 29 | virtual void buttonHeldRepeat(uint i, uint repeatCount); 30 | virtual void anyKeyPressed(); 31 | 32 | std::map holdTimes; 33 | std::map holdAndRepeatTimes; 34 | }; 35 | 36 | 37 | #endif //CIRCUITOS_INPUTLISTENER_H 38 | -------------------------------------------------------------------------------- /src/Input/InputShift.cpp: -------------------------------------------------------------------------------- 1 | #include "InputShift.h" 2 | 3 | #define PERIOD 1 4 | #define LH(pin) do { digitalWrite((pin), LOW); delayMicroseconds(PERIOD); digitalWrite((pin), HIGH); delayMicroseconds(PERIOD); } while(0) 5 | #define HL(pin) do { digitalWrite((pin), HIGH); delayMicroseconds(PERIOD); digitalWrite((pin), LOW); delayMicroseconds(PERIOD); } while(0) 6 | 7 | InputShift::InputShift(uint8_t dataPin, uint8_t clockPin, uint8_t loadPin, uint8_t numButtons) : dataPin(dataPin), clockPin(clockPin), loadPin(loadPin), 8 | numButtons(numButtons), numShifts(ceil((float) numButtons / 8.0f)), Input(numButtons){ 9 | 10 | states.resize(numShifts * 8, true); 11 | 12 | for(int i = 0; i < numButtons; i++){ 13 | InputShift::registerButton(i); 14 | } 15 | } 16 | 17 | void InputShift::begin(){ 18 | pinMode(dataPin, INPUT); 19 | pinMode(clockPin, OUTPUT); 20 | pinMode(loadPin, OUTPUT); 21 | } 22 | 23 | void InputShift::scanButtons(){ 24 | digitalWrite(clockPin, LOW); 25 | LH(loadPin); 26 | 27 | for(int i = 0; i < numShifts * 8; i++){ 28 | states[numShifts * 8 - i - 1] = digitalRead(dataPin) == HIGH; 29 | HL(clockPin); 30 | } 31 | 32 | for(int i = 0; i < numButtons; i++){ 33 | if(states[i]){ 34 | Input::btnRelease(i); 35 | }else{ 36 | Input::btnPress(i); 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/Input/InputShift.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_INPUTSHIFT_H 2 | #define CIRCUITOS_INPUTSHIFT_H 3 | 4 | #include "Input.h" 5 | 6 | class InputShift : public Input { 7 | public: 8 | InputShift(uint8_t dataPin, uint8_t clockPin, uint8_t loadPin, uint8_t numButtons); 9 | void begin(); 10 | 11 | protected: 12 | void scanButtons() override; 13 | 14 | private: 15 | uint8_t dataPin; 16 | uint8_t clockPin; 17 | uint8_t loadPin; 18 | 19 | uint8_t numButtons; 20 | uint8_t numShifts; 21 | 22 | std::vector states; 23 | 24 | }; 25 | 26 | #endif //CIRCUITOS_INPUTSHIFT_H 27 | -------------------------------------------------------------------------------- /src/Loop/LoopListener.cpp: -------------------------------------------------------------------------------- 1 | #include "LoopListener.h" 2 | #include "LoopManager.h" 3 | 4 | #ifdef CIRCUITOS_TASK 5 | #include 6 | #include 7 | #endif 8 | 9 | LoopListener::LoopListener(){ 10 | // LoopManager::addListener(this); 11 | } 12 | 13 | LoopListener::~LoopListener(){ 14 | LoopManager::removeListener(this); 15 | } 16 | 17 | #ifdef CIRCUITOS_TASK 18 | void LoopListener::startTask(std::string name, byte priority, size_t stackSize){ 19 | lastMicros = micros(); 20 | task = new Task(std::move(name), taskFunc, stackSize, this); 21 | task->start(priority); 22 | } 23 | 24 | void LoopListener::stopTask(){ 25 | if(task == nullptr || !task->running) return; 26 | task->stop(true); 27 | delete task; 28 | task = nullptr; 29 | } 30 | 31 | void LoopListener::taskFunc(Task* task){ 32 | LoopListener* instance = static_cast(task->arg); 33 | 34 | while(task->running){ 35 | vTaskDelay(1); 36 | 37 | uint m = micros(); 38 | uint delta = m - instance->lastMicros; 39 | 40 | instance->loop(delta); 41 | 42 | instance->lastMicros = m; 43 | } 44 | } 45 | #endif -------------------------------------------------------------------------------- /src/Loop/LoopListener.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_LOOPLISTENER_H 2 | #define CIRCUITOS_LOOPLISTENER_H 3 | 4 | #include 5 | #include "../../Setup.hpp" 6 | 7 | #ifdef CIRCUITOS_TASK 8 | #include 9 | 10 | class Task; 11 | #endif 12 | 13 | class LoopListener { 14 | public: 15 | LoopListener(); 16 | virtual ~LoopListener(); 17 | 18 | virtual void loop(uint micros) = 0; 19 | 20 | #ifdef CIRCUITOS_TASK 21 | void startTask(std::string name, byte priority = 0, size_t stackSize = 2048); 22 | void stopTask(); 23 | static void taskFunc(Task* task); 24 | #endif 25 | 26 | private: 27 | 28 | #ifdef CIRCUITOS_TASK 29 | Task* task = nullptr; 30 | uint lastMicros = 0; 31 | #endif 32 | 33 | }; 34 | 35 | 36 | #endif //CIRCUITOS_LOOPLISTENER_H 37 | -------------------------------------------------------------------------------- /src/Loop/LoopManager.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_LOOPMANAGER_H 2 | #define CIRCUITOS_LOOPMANAGER_H 3 | 4 | #include 5 | #include 6 | #include "../../Setup.hpp" 7 | #include 8 | 9 | class LoopListener; 10 | class Task; 11 | 12 | class LoopManager { 13 | public: 14 | /** 15 | * Reserve listener vector slots. 16 | * @param count Expected listener count 17 | */ 18 | static void reserve(size_t count); 19 | 20 | static void addListener(LoopListener* listener); 21 | static void removeListener(LoopListener* listener); 22 | 23 | static void loop(); 24 | 25 | static void resetTime(); 26 | 27 | /** 28 | * Run later. The function will be ran after the next loop iteration is completed. 29 | */ 30 | static void defer(std::function); 31 | 32 | #ifdef CIRCUITOS_TASK 33 | /** 34 | * Start the LoopManager as a separate task. The default stack size for the task is 10kb. 35 | * @see LoopManager::setStackSize 36 | * @param priority 37 | */ 38 | static void startTask(byte priority = 0, byte core = 1); 39 | 40 | /** 41 | * Stops the LoopManager task. 42 | */ 43 | static void stopTask(); 44 | 45 | /** 46 | * Set the stack size for the LoopManager task. Applicable only when running in a separate task, and will stop 47 | * the task if it's running. 48 | * @see LoopManager::startTask 49 | * @param size Stack size in bytes. 50 | */ 51 | static void setStackSize(size_t size); 52 | 53 | static void taskFunc(Task* task); 54 | #endif 55 | 56 | private: 57 | static std::unordered_set listeners; 58 | static std::unordered_set addedListeners; 59 | static std::unordered_set removedListeners; 60 | 61 | volatile static bool iterating; 62 | static uint lastMicros; 63 | static void insertListeners(); 64 | static void clearListeners(); 65 | 66 | static std::vector> deferred; 67 | 68 | #ifdef CIRCUITOS_TASK 69 | static Task* task; 70 | #endif 71 | 72 | }; 73 | 74 | 75 | #endif //CIRCUITOS_LOOPMANAGER_H 76 | -------------------------------------------------------------------------------- /src/Motion/MPU.cpp: -------------------------------------------------------------------------------- 1 | #include "MPU.h" 2 | 3 | void MPU::calibrate(){ 4 | euler = { 0, 0, 0 }; 5 | velocity = { 0, 0, 0 }; 6 | } 7 | 8 | const vec3f& MPU::getEuler() const { 9 | return euler; 10 | } 11 | 12 | const quatf& MPU::getQuat() const{ 13 | return quatRot; 14 | } 15 | 16 | const vec3f& MPU::getGyro() const{ 17 | return gyro; 18 | } 19 | 20 | const vec3f& MPU::getAccel() const{ 21 | return accel; 22 | } 23 | 24 | const vec3f& MPU::getMagn() const{ 25 | return magn; 26 | } 27 | 28 | const vec3f& MPU::getVelocity() const{ 29 | return velocity; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/Motion/MPU.h: -------------------------------------------------------------------------------- 1 | // Courtesy of https://github.com/kriswiner/MPU9250 2 | 3 | #ifndef SETTINGS_MPU_H 4 | #define SETTINGS_MPU_H 5 | 6 | #include 7 | #include "vec.hpp" 8 | 9 | class MPU { 10 | public: 11 | virtual bool readSensor() = 0; 12 | virtual void begin() = 0; 13 | virtual void calibrate(); 14 | 15 | virtual const vec3f& getEuler() const; 16 | virtual const quatf& getQuat() const; 17 | virtual const vec3f& getGyro() const; 18 | virtual const vec3f& getAccel() const; 19 | virtual const vec3f& getMagn() const; 20 | virtual const vec3f& getVelocity() const; 21 | 22 | 23 | protected: 24 | vec3f euler; 25 | quatf quatRot; 26 | vec3f gyro; 27 | vec3f accel; 28 | vec3f magn; 29 | vec3f velocity; 30 | 31 | 32 | }; 33 | 34 | 35 | #endif //SETTINGS_MPU_H 36 | -------------------------------------------------------------------------------- /src/Motion/MPUFusion.cpp: -------------------------------------------------------------------------------- 1 | #include "MPUFusion.h" 2 | #include "Fusion.hpp" 3 | 4 | MPUFusion::MPUFusion(MPU* mpu) : mpu(mpu){ } 5 | 6 | void MPUFusion::begin(){ 7 | mpu->begin(); 8 | } 9 | 10 | void MPUFusion::calibrate(){ 11 | MPU::calibrate(); 12 | quatRot = { 1, 1, 1, 0 }; 13 | mpu->calibrate(); 14 | } 15 | 16 | bool MPUFusion::readSensor(){ 17 | return true; 18 | } 19 | 20 | void MPUFusion::loop(uint millis){ 21 | time += millis; 22 | 23 | if(time >= pollT * 1000){ 24 | //Serial.printf("Polling after %.2f ms, %.2fHz\n", (float) time / 1000.0f, 1000000.f / time); 25 | 26 | /*while(calced < stepRate){ 27 | calc(); 28 | calced++; 29 | }*/ 30 | 31 | poll(); 32 | time = 0; 33 | calced = 0; 34 | } 35 | 36 | /*if(time >= calced * stepT){ 37 | calc(); 38 | calced++; 39 | }*/ 40 | } 41 | 42 | uint lastm = 0; 43 | void MPUFusion::poll(){ 44 | uint d = micros() - lastm; 45 | lastm = micros(); 46 | //Serial.printf("Polling after %.2fms, f = %.2fHz\n", (float) d / 1000.0f, 1000000.0f / d); 47 | 48 | mpu->readSensor(); 49 | tmpAccel = mpu->getAccel(); 50 | tmpGyro = mpu->getGyro(); 51 | tmpMagn = mpu->getMagn(); 52 | 53 | for(int i = 0; i < stepRate; i++){ 54 | calc(); 55 | } 56 | 57 | // mutex.lock(); 58 | accel = tmpAccel; 59 | gyro = tmpGyro; 60 | magn = tmpMagn; 61 | euler = tmpEuler; 62 | // mutex.unlock(); 63 | } 64 | 65 | void MPUFusion::calc(){ 66 | vec4f quat; 67 | MadgwickQuaternionUpdate(quat, (float) stepT / 1000.0f, sqrt(3.0f / 4.0f) * PI * (40.0f / 180.0f), 68 | -tmpAccel.y, tmpAccel.x, -tmpAccel.z, 69 | tmpGyro.pitch, tmpGyro.yaw, tmpGyro.roll, 70 | tmpMagn.x, tmpMagn.y, tmpMagn.z); 71 | 72 | 73 | tmpEuler.pitch = -atan2(2.0f * (quat[0] * quat[1] + quat[2] * quat[3]), quat[0] * quat[0] - quat[1] * quat[1] - quat[2] * quat[2] + quat[3] * quat[3]); 74 | tmpEuler.yaw = atan2(2.0f * (quat[1] * quat[2] + quat[0] * quat[3]), quat[0] * quat[0] + quat[1] * quat[1] - quat[2] * quat[2] - quat[3] * quat[3]); 75 | tmpEuler.roll = -asin(2.0f * (quat[1] * quat[3] - quat[0] * quat[2])); 76 | tmpEuler.yaw -= 0.0f ; // Declination at Danville, California is 13 degrees 48 minutes and 47 seconds on 2014-04-04 77 | 78 | // TODO: check if fusion function assumes quat[0] is w component 79 | this->quatRot = { quat[0], quat[1], quat[2], quat[3] }; 80 | 81 | // accel.x += 1.0f * sin(euler.roll); 82 | // accel.y -= 1.0f * sin(euler.pitch); 83 | //accel.z -= 1.0f * (cos(euler.pitch) * sin(euler.roll) + cos(euler.roll) * sin(euler.pitch)); 84 | 85 | /*float o = 0.9f; 86 | gravity.x = o * gravity.x + (1.0f - o) * accel.x; 87 | gravity.y = o * gravity.y + (1.0f - o) * accel.y; 88 | gravity.z = o * gravity.z + (1.0f - o) * accel.z; 89 | 90 | accel.x -= gravity.x; 91 | accel.y -= gravity.y; 92 | accel.z -= gravity.z;*/ 93 | 94 | /*velocity.x += accel.x * dt; 95 | velocity.y += accel.y * dt; 96 | velocity.z += accel.z * dt;*/ 97 | } -------------------------------------------------------------------------------- /src/Motion/MPUFusion.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MPUFUSION_H 2 | #define CIRCUITOS_MPUFUSION_H 3 | 4 | 5 | #include "../Loop/LoopListener.h" 6 | #include "MPU.h" 7 | 8 | class MPUFusion : public MPU, public LoopListener { 9 | public: 10 | explicit MPUFusion(MPU* mpu); 11 | 12 | void loop(uint millis) override; 13 | 14 | bool readSensor() override; 15 | void begin() override; 16 | void calibrate() override; 17 | 18 | private: 19 | MPU* mpu = nullptr; 20 | 21 | const uint pollT = 1000 / 500; // in ms 22 | const uint stepRate = 2; 23 | const uint stepT = pollT / stepRate; 24 | uint time = 0; 25 | uint calced = 0; 26 | 27 | vec3f gravity = { 0, 0, 0 }; 28 | 29 | void calc(); 30 | void poll(); 31 | 32 | vec3f tmpEuler = { 0, 0, 0 }; 33 | vec3f tmpGyro = { 0, 0, 0 }; 34 | vec3f tmpAccel = { 0, 0, 0 }; 35 | vec3f tmpMagn = { 0, 0, 0 }; 36 | }; 37 | 38 | 39 | #endif //CIRCUITOS_MPUFUSION_H 40 | -------------------------------------------------------------------------------- /src/Network/Net.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_NET 4 | #include "Net.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Network/Net.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_NET_H 2 | #define CIRCUITOS_NET_H 3 | 4 | #include 5 | #include "../Loop/LoopListener.h" 6 | #include "../Util/Vector.h" 7 | 8 | class NetStateListener { 9 | private: virtual void state(wl_status_t) = 0; 10 | friend class NetImpl; 11 | }; 12 | 13 | class NetImpl : public LoopListener { 14 | public: 15 | NetImpl(); 16 | 17 | enum NetError { OK, WIFI, NET, SERVICE }; 18 | 19 | void set(const char* ssid, const char* pass); 20 | 21 | /** 22 | * A void function that takes a wl_status_t argument. 23 | */ 24 | typedef void (NetStateCallback)(wl_status_t); 25 | 26 | /** 27 | * Connects to the WiFi in a non-blocking fashion, in the loop thread. 3 tries, 5s timeout. 28 | * Starts the WiFi animation. Will not trigger disconnect callbacks/listeners. Always check 29 | * Net.connected() before making network requests. 30 | * 31 | * @see Net.connect() 32 | * 33 | * @param resultCallback will be called in the Loop thread 34 | */ 35 | void connect(NetStateCallback* resultCallback = nullptr); 36 | 37 | void loop(uint micros) override; 38 | 39 | /** 40 | * Register a state callback. This will not trigger when the network goes down for reconnection. 41 | * Always check Net.connected() before making network requests. 42 | * @param callback Will be called when connection goes up or down. 43 | */ 44 | void addStateCallback(NetStateCallback* callback); 45 | 46 | /** 47 | * Register a state listener. This will not trigger when the network goes down for reconnection. 48 | * Always check Net.checkConnection() before making network requests. 49 | * @param NetEventListener::state will be called when connection goes up or down. 50 | */ 51 | void addStateListener(NetStateListener* listener); 52 | 53 | void removeStateListener(NetStateListener* listener); 54 | 55 | /** 56 | * Checks if a working Internet connection is established. First, by checking if the WiFi is connected, 57 | * and then trying to establish a connection to specified URL (defaults to spencer.circuitmess.com). This is a blocking function. 58 | * @param url URL to be pinged. Defaults to spencer.circuitmess.com 59 | * @return True is all OK, false otherwise 60 | */ 61 | bool checkConnection(const char* url = "http://spencer.circuitmess.com:7979/index"); 62 | 63 | /** 64 | * Reconnects the WiFi in a blocking fashion. 2 tries, 5s timeout 65 | * @return True if succeeded, false otherwise 66 | */ 67 | bool reconnect(); 68 | 69 | wl_status_t getState() const; 70 | 71 | NetError getLastError() const; 72 | 73 | private: 74 | const uint timeouts[3] = { 3000, 7000, 10000 }; 75 | 76 | const char* ssid; 77 | const char* pass; 78 | 79 | void checkRemoveListeners(); 80 | Vector stateListeners; 81 | Vector removeStateListeners; 82 | std::vector stateCallbacks; 83 | 84 | NetStateCallback* connectResultCallback = nullptr; 85 | 86 | void setState(wl_status_t state); 87 | 88 | void tryConnect(); 89 | void retryConnect(); 90 | bool connecting = false; 91 | uint connectTime; 92 | uint8_t connectRetries; 93 | 94 | wl_status_t state = WL_DISCONNECTED; 95 | 96 | NetError lastError = OK; 97 | }; 98 | 99 | extern NetImpl Net; 100 | 101 | #endif //CIRCUITOS_NET_H 102 | -------------------------------------------------------------------------------- /src/Network/StreamableHTTPClient.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_NET 4 | #include "StreamableHTTPClient.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Network/StreamableHTTPClient.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_STRHTTPCLIENT_H 2 | #define CIRCUITOS_STRHTTPCLIENT_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | class StreamableHTTPClient : public HTTPClient { 9 | public: 10 | bool startPOST(); 11 | bool send(uint8_t* data, uint32_t size); 12 | int finish(); 13 | 14 | private: 15 | Vector buffer; 16 | const uint32_t bufferSize = 800; 17 | 18 | bool sendBuffer(); 19 | }; 20 | 21 | 22 | #endif //CIRCUITOS_STRHTTPCLIENT_H 23 | -------------------------------------------------------------------------------- /src/Network/StreamableHTTPClient.impl: -------------------------------------------------------------------------------- 1 | #include "StreamableHTTPClient.h" 2 | 3 | bool StreamableHTTPClient::startPOST(){ 4 | // connect to server 5 | if(!connect()) { 6 | return false; 7 | } 8 | 9 | // send Header 10 | if(!sendHeader("POST")) { 11 | return false; 12 | } 13 | 14 | buffer.clear(); 15 | buffer.reserve(bufferSize); 16 | 17 | return true; 18 | } 19 | 20 | bool StreamableHTTPClient::send(uint8_t* data, uint32_t size){ 21 | int curSize = buffer.size(); 22 | buffer.resize(curSize + size); 23 | memcpy(buffer.data() + curSize, data, size); 24 | 25 | if(buffer.size() >= bufferSize){ 26 | return sendBuffer(); 27 | } 28 | 29 | return true; 30 | } 31 | 32 | int StreamableHTTPClient::finish(){ 33 | sendBuffer(); 34 | buffer.reserve(0); 35 | return returnError(handleHeaderResponse()); 36 | } 37 | 38 | bool StreamableHTTPClient::sendBuffer(){ 39 | if(buffer.empty()) return true; 40 | 41 | if(_client->write(buffer.data(), buffer.size()) != buffer.size()){ 42 | return false; 43 | } 44 | 45 | buffer.clear(); 46 | return true; 47 | } 48 | -------------------------------------------------------------------------------- /src/Support/Context.cpp: -------------------------------------------------------------------------------- 1 | #include "Context.h" 2 | 3 | bool Context::deleteOnPop = false; 4 | Context* Context::currentContext = nullptr; 5 | 6 | Context::Context(Display& display) : screen(display){ 7 | addSprite(&screen); 8 | } 9 | 10 | Context::~Context(){ 11 | 12 | } 13 | 14 | Screen& Context::getScreen(){ 15 | return screen; 16 | } 17 | 18 | void Context::addSprite(SpriteElement* sprite){ 19 | sprites.push_back(sprite); 20 | } 21 | 22 | void Context::pack(){ 23 | currentContext = nullptr; 24 | if(packed) return; 25 | 26 | for(SpriteElement* element : sprites){ 27 | element->pack(); 28 | } 29 | 30 | packed = true; 31 | deinit(); 32 | } 33 | 34 | void Context::unpack(){ 35 | currentContext = this; 36 | if(!packed) return; 37 | 38 | for(SpriteElement* element : sprites){ 39 | element->unpack(); 40 | } 41 | 42 | packed = false; 43 | init(); 44 | } 45 | 46 | ContextTransition* Context::pop(){ 47 | if(parent == nullptr) return nullptr; 48 | ContextTransition* transition = new ContextTransition(*screen.getDisplay(), this, parent, true); 49 | if(deleteOnPop){ 50 | transition->setDoneCallback([](Context* oldCtx, Context* newCtx){ 51 | delete oldCtx; 52 | }); 53 | } 54 | parent = nullptr; 55 | return transition; 56 | } 57 | 58 | ContextTransition* Context::pop(void* data){ 59 | if(parent == nullptr) return nullptr; 60 | parent->returned(data); 61 | pop(); 62 | } 63 | 64 | ContextTransition* Context::push(Context* parent){ 65 | this->parent = parent; 66 | return new ContextTransition(*screen.getDisplay(), parent, this); 67 | 68 | } 69 | 70 | void Context::returned(void* data){ 71 | 72 | } 73 | 74 | void Context::setDeleteOnPop(bool deleteOnPop){ 75 | Context::deleteOnPop = deleteOnPop; 76 | } 77 | 78 | void Context::setParent(Context* parent){ 79 | Context::parent = parent; 80 | } 81 | 82 | void Context::init(){} 83 | 84 | void Context::deinit(){} 85 | 86 | Context *Context::getCurrentContext(){ 87 | return currentContext; 88 | } 89 | -------------------------------------------------------------------------------- /src/Support/Context.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_CONTEXT_H 2 | #define CIRCUITOS_CONTEXT_H 3 | 4 | #include "../UI/Screen.h" 5 | #include "ContextTransition.h" 6 | 7 | class Context { 8 | public: 9 | explicit Context(Display& display); 10 | virtual ~Context(); 11 | 12 | virtual void draw() = 0; 13 | 14 | virtual void start() = 0; 15 | virtual void stop() = 0; 16 | 17 | virtual void addSprite(SpriteElement* sprite); 18 | virtual void pack(); 19 | virtual void unpack(); 20 | 21 | virtual ContextTransition* pop(); 22 | virtual ContextTransition* pop(void* data); 23 | virtual ContextTransition* push(Context* parent); 24 | virtual void returned(void* data); 25 | 26 | Screen& getScreen(); 27 | 28 | static void setDeleteOnPop(bool deleteOnPop); 29 | 30 | void setParent(Context* parent); 31 | 32 | static Context* getCurrentContext(); 33 | 34 | protected: 35 | Screen screen; 36 | Context* parent = nullptr; 37 | 38 | bool packed = false; 39 | 40 | Vector sprites; 41 | 42 | static bool deleteOnPop; 43 | 44 | virtual void init(); 45 | virtual void deinit(); 46 | 47 | private: 48 | static Context* currentContext; 49 | }; 50 | 51 | 52 | #endif //CIRCUITOS_CONTEXT_H 53 | -------------------------------------------------------------------------------- /src/Support/ContextTransition.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | #include "ContextTransition.h" 3 | 4 | bool ContextTransition::transitionRunning = false; 5 | 6 | bool ContextTransition::isRunning(){ 7 | return transitionRunning; 8 | } 9 | #ifdef CIRCUITOS_LOWRAM 10 | #include "LowRamContextTransition.impl" 11 | #else 12 | #include "StandardContextTransition.impl" 13 | #endif -------------------------------------------------------------------------------- /src/Support/ContextTransition.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_CONTEXTTRANSITION_H 2 | #define CIRCUITOS_CONTEXTTRANSITION_H 3 | 4 | #include "../Loop/LoopListener.h" 5 | #include "../Display/Display.h" 6 | 7 | class Context; 8 | 9 | class ContextTransition : public LoopListener { 10 | public: 11 | ContextTransition(Display& display, Context* contextA, Context* contextB); 12 | ContextTransition(Display& display, Context* contextA, Context* contextB, bool reverse); 13 | 14 | void setDoneCallback(void (* doneCallback)(Context*, Context*)); 15 | 16 | void loop(uint micros) override; 17 | 18 | virtual ~ContextTransition(); 19 | 20 | static bool isRunning(); 21 | 22 | private: 23 | bool reverse = false; 24 | 25 | Display* display; 26 | Context* contextA; 27 | 28 | Context* contextB; 29 | uint time = 0; 30 | 31 | uint lastScroll = 0; 32 | 33 | void copySprite(Sprite* sprite, Sprite* targetSprite, int pos); 34 | 35 | void (*doneCallback)(Context* oldCtx, Context* newCtx) = nullptr; 36 | 37 | static bool transitionRunning; 38 | }; 39 | 40 | 41 | #endif //CIRCUITOS_CONTEXTTRANSITION_H 42 | -------------------------------------------------------------------------------- /src/Support/LowRamContextTransition.impl: -------------------------------------------------------------------------------- 1 | #include "ContextTransition.h" 2 | #include "Context.h" 3 | #include "../Loop/LoopManager.h" 4 | #include "../UI/Screen.h" 5 | 6 | ContextTransition::ContextTransition(Display& display, Context* contextA, Context* contextB) : 7 | ContextTransition(display, contextA, contextB, false){ 8 | 9 | } 10 | 11 | ContextTransition::ContextTransition(Display& display, Context* contextA, Context* contextB, bool reverse) 12 | : reverse(reverse), display(&display), contextA(contextA), contextB(contextB) { 13 | 14 | transitionRunning = true; 15 | contextA->stop(); 16 | contextA->pack(); 17 | 18 | contextB->unpack(); 19 | contextB->draw(); 20 | 21 | LoopManager::addListener(this); 22 | } 23 | 24 | ContextTransition::~ContextTransition(){ 25 | transitionRunning = false; 26 | LoopManager::removeListener(this); 27 | } 28 | 29 | void ContextTransition::loop(uint micros){ 30 | if(time == 0){ 31 | time+=1; 32 | return; 33 | } 34 | time += micros; 35 | 36 | uint scroll = time / (1.5 * 1000.0); 37 | if(lastScroll < scroll){ 38 | if(reverse){ 39 | display->getBaseSprite()->setPos(0, min(0, (int) scroll - (int) display->getHeight())); 40 | }else{ 41 | display->getBaseSprite()->setPos(0, max(0, (int) display->getHeight() - (int) scroll)); 42 | } 43 | display->commit(); 44 | 45 | lastScroll = scroll; 46 | } 47 | 48 | if(scroll >= display->getHeight()){ 49 | display->getBaseSprite()->setPos(0,0); 50 | display->commit(); 51 | 52 | if(doneCallback){ 53 | doneCallback(contextA, contextB); 54 | } 55 | 56 | contextB->start(); 57 | delete this; 58 | } 59 | } 60 | 61 | void ContextTransition::setDoneCallback(void (* doneCallback)(Context*, Context*)){ 62 | ContextTransition::doneCallback = doneCallback; 63 | } 64 | -------------------------------------------------------------------------------- /src/Support/Modal.cpp: -------------------------------------------------------------------------------- 1 | #include "Modal.h" 2 | #include "ModalTransition.h" 3 | 4 | Modal* Modal::currentModal = nullptr; 5 | 6 | Modal::Modal(Context& context, uint width, uint height) : Context(*context.getScreen().getDisplay()){ 7 | screen.getSprite()->resize(width, height); 8 | 9 | posX = (context.getScreen().getDisplay()->getWidth() - width) / 2; 10 | posY = (context.getScreen().getDisplay()->getHeight() - height) / 2; 11 | 12 | setPos(posX, posY); 13 | } 14 | 15 | void Modal::pack(){ 16 | currentModal = nullptr; 17 | if(packed) return; 18 | 19 | for(SpriteElement* element : sprites){ 20 | element->pack(); 21 | } 22 | 23 | packed = true; 24 | deinit(); 25 | } 26 | 27 | void Modal::unpack(){ 28 | currentModal = this; 29 | if(!packed) return; 30 | 31 | for(SpriteElement* element : sprites){ 32 | element->unpack(); 33 | } 34 | 35 | packed = false; 36 | init(); 37 | } 38 | 39 | ContextTransition* Modal::push(Context* parent){ 40 | this->parent = parent; 41 | return static_cast((void*)new ModalTransition(*screen.getDisplay(), parent, this)); 42 | } 43 | 44 | ContextTransition* Modal::pop(){ 45 | if(parent == nullptr) return nullptr; 46 | ModalTransition* transition = new ModalTransition(*screen.getDisplay(), parent, this, true); 47 | parent = nullptr; 48 | return static_cast((void*)transition); 49 | } 50 | 51 | ContextTransition* Modal::pop(void* data){ 52 | if(parent == nullptr) return nullptr; 53 | parent->returned(data); 54 | pop(); 55 | } 56 | 57 | int Modal::getPosX() const{ 58 | return posX; 59 | } 60 | 61 | int Modal::getPosY() const{ 62 | return posY; 63 | } 64 | 65 | void Modal::setPos(int posX, int posY){ 66 | Modal::posX = posX; 67 | Modal::posY = posY; 68 | 69 | getScreen().setPos(posX, posY); 70 | } 71 | 72 | Modal *Modal::getCurrentModal(){ 73 | return currentModal; 74 | } 75 | -------------------------------------------------------------------------------- /src/Support/Modal.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MODAL_H 2 | #define CIRCUITOS_MODAL_H 3 | 4 | 5 | #include "Context.h" 6 | #include "ModalTransition.h" 7 | 8 | class Modal : public Context { 9 | public: 10 | Modal(Context& context, uint width, uint height); 11 | 12 | void pack() override; 13 | void unpack() override; 14 | 15 | ContextTransition* push(Context* parent) override; 16 | ContextTransition* pop() override; 17 | ContextTransition* pop(void* data) override; 18 | 19 | int getPosX() const; 20 | int getPosY() const; 21 | 22 | void setPos(int posX,int posY); 23 | 24 | static Modal* getCurrentModal(); 25 | 26 | 27 | 28 | private: 29 | int posX = 0; 30 | int posY = 0; 31 | 32 | static Modal* currentModal; 33 | 34 | 35 | }; 36 | 37 | 38 | #endif //CIRCUITOS_MODAL_H 39 | -------------------------------------------------------------------------------- /src/Support/ModalTransition.cpp: -------------------------------------------------------------------------------- 1 | #include "ModalTransition.h" 2 | #include "../Loop/LoopManager.h" 3 | #include "Modal.h" 4 | #include "Context.h" 5 | bool ModalTransition::transitionRunning = false; 6 | bool ModalTransition::deleteOnPop = true; 7 | 8 | ModalTransition::ModalTransition(Display& display, Context* context, Modal* modal, bool reverse) 9 | : reverse(reverse), display(&display), context(context), modal(modal){ 10 | 11 | transitionRunning = true; 12 | modalX = modal->getScreen().getX(); 13 | modalY = modal->getScreen().getY(); 14 | contextX = context->getScreen().getX(); 15 | contextY = context->getScreen().getY(); 16 | 17 | 18 | if(reverse){ 19 | modal->stop(); 20 | 21 | context->unpack(); 22 | context->draw(); 23 | 24 | modal->getScreen().getSprite()->setPos(modalX, modalY); 25 | }else{ 26 | context->stop(); 27 | 28 | modal->unpack(); 29 | modal->draw(); 30 | 31 | modal->getScreen().getSprite()->setPos(modalX, display.getHeight()); 32 | } 33 | LoopManager::addListener(this); 34 | } 35 | 36 | ModalTransition::~ModalTransition(){ 37 | transitionRunning = false; 38 | LoopManager::removeListener(this); 39 | } 40 | 41 | void ModalTransition::copySprite(Sprite* sprite, Sprite* targetSprite, int x, int y){ 42 | Sprite* oldParent = sprite->getParent(); 43 | uint oldX = sprite->getX(); 44 | uint oldY = sprite->getY(); 45 | 46 | sprite->setParent(targetSprite); 47 | sprite->setPos(x, y); 48 | 49 | sprite->push(); 50 | 51 | sprite->setParent(oldParent); 52 | sprite->setPos(oldX, oldY); 53 | } 54 | 55 | void ModalTransition::loop(uint micros){ 56 | if(time == 0){ 57 | time+=1; 58 | return; 59 | } 60 | time += micros; 61 | 62 | int scroll = time / (1.5 * 1000.0); 63 | if(lastScroll < scroll){ 64 | copySprite(context->getScreen().getSprite(), display->getBaseSprite(), contextX, contextY); 65 | 66 | if(reverse){ 67 | copySprite(context->getScreen().getSprite(), display->getBaseSprite(), contextX, contextY); 68 | copySprite(modal->getScreen().getSprite(), display->getBaseSprite(), modalX, modalY + scroll); 69 | }else{ 70 | copySprite(context->getScreen().getSprite(), display->getBaseSprite(), contextX, contextY); 71 | copySprite(modal->getScreen().getSprite(), display->getBaseSprite(), modalX, max(modalY, (int) display->getHeight() - scroll)); 72 | 73 | } 74 | 75 | lastScroll = scroll; 76 | } 77 | 78 | if(scroll >= (display->getHeight() - modalY)){ 79 | modal->getScreen().setPos(modalX, modalY); 80 | 81 | if(reverse){ 82 | modal->pack(); 83 | if(deleteOnPop){ 84 | delete modal; 85 | } 86 | context->unpack(); 87 | context->getScreen().commit(); 88 | context->start(); 89 | }else{ 90 | context->pack(); 91 | modal->unpack(); 92 | modal->getScreen().commit(); 93 | modal->start(); 94 | } 95 | 96 | if(doneCallback){ 97 | doneCallback(context, modal); 98 | } 99 | 100 | delete this; 101 | }else{ 102 | display->commit(); 103 | } 104 | } 105 | 106 | bool ModalTransition::isRunning(){ 107 | return transitionRunning; 108 | } 109 | 110 | void ModalTransition::setDoneCallback(void (*doneCallback)(Context*, Modal*)){ 111 | ModalTransition::doneCallback = doneCallback; 112 | } 113 | 114 | void ModalTransition::setDeleteOnPop(bool _deleteOnPop){ 115 | deleteOnPop = _deleteOnPop; 116 | } 117 | -------------------------------------------------------------------------------- /src/Support/ModalTransition.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MODALTRANSITION_H 2 | #define CIRCUITOS_MODALTRANSITION_H 3 | 4 | #include "../Loop/LoopListener.h" 5 | #include "../UI/ElementContainer.h" 6 | #include "../UI/LinearLayout.h" 7 | #include "../UI/ScrollLayout.h" 8 | #include "../UI/Image.h" 9 | class Modal; 10 | class Context; 11 | 12 | class ModalTransition : public LoopListener { 13 | public: 14 | ModalTransition(Display& display, Context* context, Modal* modal, bool reverse = false); 15 | 16 | void loop(uint micros) override; 17 | 18 | virtual ~ModalTransition(); 19 | 20 | static bool isRunning(); 21 | 22 | void setDoneCallback(void (* doneCallback)(Context*, Modal*)); 23 | 24 | static void setDeleteOnPop(bool deleteOnPop); 25 | 26 | private: 27 | bool reverse = false; 28 | 29 | Display* display; 30 | Context* context; 31 | Modal* modal; 32 | 33 | uint time = 0; 34 | uint lastScroll = 0; 35 | 36 | int modalX, modalY, contextX, contextY; 37 | 38 | void copySprite(Sprite* sprite, Sprite* targetSprite, int x = 0, int y = 0); 39 | 40 | static bool transitionRunning; 41 | 42 | void (*doneCallback)(Context* oldCtx, Modal* newCtx) = nullptr; 43 | 44 | static bool deleteOnPop; 45 | }; 46 | 47 | 48 | #endif //CIRCUITOS_MODALTRANSITION_H 49 | -------------------------------------------------------------------------------- /src/Support/StandardContextTransition.impl: -------------------------------------------------------------------------------- 1 | #include "ContextTransition.h" 2 | #include "Context.h" 3 | #include "../Loop/LoopManager.h" 4 | #include "../UI/Screen.h" 5 | 6 | ContextTransition::ContextTransition(Display& display, Context* contextA, Context* contextB) : 7 | ContextTransition(display, contextA, contextB, false){ 8 | 9 | } 10 | 11 | ContextTransition::ContextTransition(Display& display, Context* contextA, Context* contextB, bool reverse) 12 | : reverse(reverse), display(&display), contextA(contextA), contextB(contextB) { 13 | 14 | transitionRunning = true; 15 | contextA->stop(); 16 | contextA->pack(); 17 | 18 | contextB->unpack(); 19 | contextB->getScreen().getSprite()->setPos(0, display.getHeight()); 20 | contextB->draw(); 21 | 22 | LoopManager::addListener(this); 23 | } 24 | 25 | ContextTransition::~ContextTransition(){ 26 | LoopManager::removeListener(this); 27 | transitionRunning = false; 28 | } 29 | 30 | void ContextTransition::copySprite(Sprite* sprite, Sprite* targetSprite, int pos){ 31 | Sprite* oldParent = sprite->getParent(); 32 | uint oldX = sprite->getX(); 33 | uint oldY = sprite->getY(); 34 | 35 | sprite->setParent(targetSprite); 36 | sprite->setPos(0, pos); 37 | 38 | sprite->push(); 39 | 40 | sprite->setParent(oldParent); 41 | sprite->setPos(oldX, oldY); 42 | } 43 | 44 | void ContextTransition::loop(uint micros){ 45 | if(time == 0){ 46 | time+=1; 47 | return; 48 | } 49 | time += micros; 50 | 51 | uint scroll = time / (1.5 * 1000.0); 52 | if(lastScroll < scroll){ 53 | 54 | if(reverse){ 55 | //copySprite(display->getBaseSprite(), display->getBaseSprite(), 1); 56 | copySprite(contextB->getScreen().getSprite(), display->getBaseSprite(), min(0, (int) scroll - (int) display->getHeight())); 57 | }else{ 58 | copySprite(display->getBaseSprite(), display->getBaseSprite(), -1); 59 | copySprite(contextB->getScreen().getSprite(), display->getBaseSprite(), max(0, (int) display->getHeight() - (int) scroll)); 60 | } 61 | 62 | lastScroll = scroll; 63 | } 64 | 65 | if(scroll >= display->getHeight()){ 66 | contextB->getScreen().setPos(0, 0); 67 | contextB->getScreen().commit(); 68 | 69 | if(doneCallback){ 70 | doneCallback(contextA, contextB); 71 | } 72 | 73 | contextB->start(); 74 | delete this; 75 | }else{ 76 | display->commit(); 77 | } 78 | } 79 | 80 | void ContextTransition::setDoneCallback(void (* doneCallback)(Context*, Context*)){ 81 | ContextTransition::doneCallback = doneCallback; 82 | } 83 | -------------------------------------------------------------------------------- /src/Sync/BinarySemaphone.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_BINARY_SEMAPHORE 4 | #include "BinarySemaphore.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Sync/BinarySemaphore.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_BINARYSEMAPHORE_H 2 | #define CIRCUITOS_BINARYSEMAPHORE_H 3 | 4 | #define INCLUDE_vTaskSuspend 1 5 | #include 6 | #include 7 | 8 | class BinarySemaphore { 9 | public: 10 | BinarySemaphore(); 11 | ~BinarySemaphore(); 12 | 13 | bool wait(); 14 | void signal(); 15 | 16 | private: 17 | SemaphoreHandle_t semaphore; 18 | }; 19 | 20 | 21 | #endif //CIRCUITOS_BINARYSEMAPHORE_H 22 | -------------------------------------------------------------------------------- /src/Sync/BinarySemaphore.impl: -------------------------------------------------------------------------------- 1 | #include "BinarySemaphore.h" 2 | 3 | BinarySemaphore::BinarySemaphore(){ 4 | semaphore = xSemaphoreCreateBinary(); 5 | } 6 | 7 | BinarySemaphore::~BinarySemaphore(){ 8 | vSemaphoreDelete(semaphore); 9 | } 10 | 11 | bool BinarySemaphore::wait(){ 12 | return xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE; // TODO: INCLUDE_vTaskSuspend to be set to 1 13 | } 14 | 15 | void BinarySemaphore::signal(){ 16 | xSemaphoreGive(semaphore); 17 | } 18 | -------------------------------------------------------------------------------- /src/Sync/Mutex.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_MUTEX 4 | #include "Mutex.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Sync/Mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_MUTEX_H 2 | #define CIRCUITOS_MUTEX_H 3 | 4 | #define INCLUDE_vTaskSuspend 1 5 | #include 6 | #include 7 | 8 | class Mutex { 9 | public: 10 | Mutex(); 11 | ~Mutex(); 12 | 13 | bool lock(); 14 | void unlock(); 15 | 16 | private: 17 | SemaphoreHandle_t mutex; 18 | }; 19 | 20 | 21 | #endif //CIRCUITOS_MUTEX_H 22 | -------------------------------------------------------------------------------- /src/Sync/Mutex.impl: -------------------------------------------------------------------------------- 1 | #include "Mutex.h" 2 | 3 | Mutex::Mutex(){ 4 | mutex = xSemaphoreCreateMutex(); 5 | } 6 | 7 | Mutex::~Mutex(){ 8 | vSemaphoreDelete(mutex); 9 | } 10 | 11 | bool Mutex::lock(){ 12 | return xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE; // TODO: INCLUDE_vTaskSuspend to be set to 1 13 | } 14 | 15 | void Mutex::unlock(){ 16 | xSemaphoreGive(mutex); 17 | } 18 | -------------------------------------------------------------------------------- /src/Sync/Queue.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_QUEUE 4 | #include "Queue.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Sync/Queue.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_QUEUE_H 2 | #define CIRCUITOS_QUEUE_H 3 | 4 | #include 5 | 6 | #define INCLUDE_vTaskSuspend 1 7 | #include 8 | 9 | class Queue { 10 | public: 11 | Queue(size_t queueSize, size_t msgSize); 12 | ~Queue(); 13 | 14 | /** 15 | * @return The number of messages in the queue. 16 | */ 17 | int count(); 18 | 19 | /** 20 | * Remove all messages from the queue. 21 | */ 22 | void reset(); 23 | 24 | /** 25 | * Return the first message in the queue. This is a non-blocking function and will return if the queue is empty. 26 | * @param buffer 27 | * @return True if the message was received, false if the queue is empty or an error occurred. 28 | */ 29 | bool peek(void* buffer); 30 | 31 | /** 32 | * Post a message to the back of the queue. This is a blocking function and will wait if the queue is full. 33 | * @param msg 34 | * @return True if the message was posted, false if an error occurred. 35 | */ 36 | bool send(void* msg); 37 | 38 | /** 39 | * Retrieve a message from the front of a queue. This is a blocking function and will wait if the queue is empty. 40 | * @param msg 41 | * @returnTrue if the message was retrieved, false if an error occurred. 42 | */ 43 | bool receive(void* msg); 44 | 45 | /** 46 | * Resize the queue. This clears all the messages in the queue. 47 | * @param queueSize 48 | */ 49 | void resize(size_t queueSize); 50 | 51 | size_t getQueueSize() const; 52 | 53 | size_t getMsgSize() const; 54 | 55 | 56 | private: 57 | QueueHandle_t queue; 58 | size_t queueSize; 59 | size_t msgSize; 60 | 61 | }; 62 | 63 | 64 | #endif //CIRCUITOS_QUEUE_H 65 | -------------------------------------------------------------------------------- /src/Sync/Queue.impl: -------------------------------------------------------------------------------- 1 | #include "Queue.h" 2 | 3 | Queue::Queue(size_t queueSize, size_t msgSize) : queueSize(queueSize), msgSize(msgSize){ 4 | queue = xQueueCreate(queueSize, msgSize); 5 | } 6 | 7 | Queue::~Queue(){ 8 | vQueueDelete(queue); 9 | } 10 | 11 | int Queue::count(){ 12 | return uxQueueMessagesWaiting(queue); 13 | } 14 | 15 | void Queue::reset(){ 16 | xQueueReset(queue); 17 | } 18 | 19 | bool Queue::peek(void* buffer){ 20 | return xQueuePeek(queue, buffer, 0); 21 | } 22 | 23 | bool Queue::send(void* msg){ 24 | return xQueueSend(queue, msg, portMAX_DELAY); 25 | } 26 | 27 | bool Queue::receive(void* msg){ 28 | return xQueueReceive(queue, msg, portMAX_DELAY); 29 | } 30 | 31 | void Queue::resize(size_t queueSize){ 32 | vQueueDelete(queue); 33 | queue = xQueueCreate(queueSize, msgSize); 34 | this->queueSize = queueSize; 35 | } 36 | 37 | size_t Queue::getQueueSize() const{ 38 | return queueSize; 39 | } 40 | 41 | size_t Queue::getMsgSize() const{ 42 | return msgSize; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/Sync/Semaphone.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_SEMAPHORE 4 | #include "Semaphore.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Sync/Semaphore.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SEMAPHORE_H 2 | #define CIRCUITOS_SEMAPHORE_H 3 | 4 | #define INCLUDE_vTaskSuspend 1 5 | #include 6 | #include 7 | 8 | class Semaphore { 9 | public: 10 | Semaphore(uint size); 11 | ~Semaphore(); 12 | 13 | bool wait(); 14 | void signal(); 15 | 16 | private: 17 | SemaphoreHandle_t semaphore; 18 | }; 19 | 20 | 21 | #endif //CIRCUITOS_SEMAPHORE_H 22 | -------------------------------------------------------------------------------- /src/Sync/Semaphore.impl: -------------------------------------------------------------------------------- 1 | #include "Semaphore.h" 2 | 3 | Semaphore::Semaphore(uint size){ 4 | semaphore = xSemaphoreCreateCounting(size, 0); 5 | } 6 | 7 | Semaphore::~Semaphore(){ 8 | vSemaphoreDelete(semaphore); 9 | } 10 | 11 | bool Semaphore::wait(){ 12 | return xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE; // TODO: INCLUDE_vTaskSuspend to be set to 1 13 | } 14 | 15 | void Semaphore::signal(){ 16 | xSemaphoreGive(semaphore); 17 | } 18 | -------------------------------------------------------------------------------- /src/UI/BitmapElement.cpp: -------------------------------------------------------------------------------- 1 | #include "BitmapElement.h" 2 | 3 | BitmapElement::BitmapElement(ElementContainer* parent, const uint16_t* bitmap, uint width, uint height) : 4 | Element(parent), bitmap(bitmap), width(width), height(height){ } 5 | 6 | uint BitmapElement::getWidth(){ 7 | return width; 8 | } 9 | 10 | uint BitmapElement::getHeight(){ 11 | return height; 12 | } 13 | 14 | void BitmapElement::draw(){ 15 | getSprite()->drawIcon(bitmap, getTotalX(), getTotalY(), width, height, 1, TFT_TRANSPARENT); 16 | Element::draw(); 17 | } 18 | -------------------------------------------------------------------------------- /src/UI/BitmapElement.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_BITMAPELEMENT_H 2 | #define CIRCUITOS_BITMAPELEMENT_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | 7 | class BitmapElement : public Element { 8 | public: 9 | BitmapElement(ElementContainer* parent, const uint16_t* bitmap, uint width, uint height); 10 | 11 | void draw() override; 12 | 13 | uint getWidth() override; 14 | uint getHeight() override; 15 | 16 | private: 17 | const uint16_t* bitmap; 18 | 19 | uint width; 20 | uint height; 21 | 22 | }; 23 | 24 | 25 | #endif //CIRCUITOS_BITMAPELEMENT_H 26 | -------------------------------------------------------------------------------- /src/UI/CacheLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "CacheLayout.h" 2 | #include "../Util/Debug.h" 3 | 4 | CacheLayout::CacheLayout(ElementContainer* parent) : Layout(parent), SpriteElement(parent->getSprite(), 0, 0){ 5 | 6 | } 7 | 8 | Sprite* CacheLayout::getSprite(){ 9 | return SpriteElement::getSprite(); 10 | } 11 | 12 | uint CacheLayout::getWidth(){ 13 | return Layout::getWidth(); 14 | } 15 | 16 | uint CacheLayout::getHeight(){ 17 | return Layout::getHeight(); 18 | } 19 | 20 | int CacheLayout::getTotalX() const{ 21 | return 0; 22 | } 23 | 24 | int CacheLayout::getTotalY() const{ 25 | return 0; 26 | } 27 | 28 | void CacheLayout::draw(){ 29 | sprite.setPos(Element::getTotalX(), Element::getTotalY()); 30 | sprite.setParent(getParent()->getSprite()); 31 | sprite.push(); 32 | } 33 | 34 | void CacheLayout::reflow(){ 35 | Layout::reflow(); 36 | sprite.resize(getWidth(), getHeight()); 37 | } 38 | 39 | void CacheLayout::refresh(){ 40 | sprite.clear(TFT_BLACK); 41 | ElementContainer::draw(); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/UI/CacheLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_CACHELAYOUT_H 2 | #define CIRCUITOS_CACHELAYOUT_H 3 | 4 | 5 | #include "../Display/Sprite.h" 6 | #include "Layout.h" 7 | #include "SpriteElement.h" 8 | 9 | class CacheLayout : public Layout, public SpriteElement { 10 | public: 11 | CacheLayout(ElementContainer* parent); 12 | 13 | Sprite* getSprite() override; 14 | 15 | uint getWidth() override; 16 | uint getHeight() override; 17 | 18 | int getTotalX() const override; 19 | int getTotalY() const override; 20 | 21 | void draw() override; 22 | void reflow() override; 23 | void refresh(); 24 | }; 25 | 26 | 27 | #endif //CIRCUITOS_CACHELAYOUT_H 28 | -------------------------------------------------------------------------------- /src/UI/CustomElement.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomElement.h" 2 | 3 | CustomElement::CustomElement(ElementContainer* parent, uint width, uint height) : Element(parent), width(width), height(height){ } 4 | 5 | uint CustomElement::getWidth(){ 6 | return width; 7 | } 8 | 9 | uint CustomElement::getHeight(){ 10 | return height; 11 | } 12 | 13 | void CustomElement::setWidth(uint width){ 14 | CustomElement::width = width; 15 | } 16 | 17 | void CustomElement::setHeight(uint height){ 18 | CustomElement::height = height; 19 | } -------------------------------------------------------------------------------- /src/UI/CustomElement.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_CUSTOMELEMENT_H 2 | #define CIRCUITOS_CUSTOMELEMENT_H 3 | 4 | #include 5 | #include "ElementContainer.h" 6 | 7 | class CustomElement : public Element { 8 | public: 9 | CustomElement(ElementContainer* parent, uint width, uint height); 10 | 11 | uint getWidth() override; 12 | uint getHeight() override; 13 | 14 | void setWidth(uint width); 15 | void setHeight(uint height); 16 | 17 | private: 18 | uint width = 0; 19 | uint height = 0; 20 | 21 | }; 22 | 23 | 24 | #endif //CIRCUITOS_CUSTOMELEMENT_H 25 | -------------------------------------------------------------------------------- /src/UI/Element.cpp: -------------------------------------------------------------------------------- 1 | #include "ElementContainer.h" 2 | #include "Element.h" 3 | #include "../Util/Debug.h" 4 | 5 | Element::Element(){ 6 | this->parent = nullptr; 7 | } 8 | 9 | Element::Element(ElementContainer* parent){ 10 | this->parent = parent; 11 | } 12 | 13 | Element::~Element(){ 14 | 15 | } 16 | 17 | ElementContainer* Element::getParent(){ 18 | return parent; 19 | } 20 | 21 | void Element::setBorder(uint width, Color color){ 22 | setBorderWidth(width); 23 | setBorderColor(color); 24 | } 25 | 26 | void Element::setBorderColor(Color borderColor){ 27 | Element::borderColor = borderColor; 28 | } 29 | 30 | void Element::setBorderWidth(uint borderWidth){ 31 | Element::borderWidth = borderWidth; 32 | } 33 | 34 | void Element::repos(){ 35 | 36 | } 37 | 38 | void Element::draw(){ 39 | logr("Drawing element: "); 40 | 41 | if(borderWidth){ 42 | logr("border "); 43 | 44 | for(int i = 0; i < borderWidth; i++){ 45 | getParent()->getSprite()->drawRect(getTotalX() + i, getTotalY() + i, getWidth() - 2*i, getHeight() - 2*i, borderColor); 46 | } 47 | } 48 | 49 | logln(); 50 | 51 | // TODO: overflow: draw over padding or erase? 52 | } 53 | 54 | int Element::getX() const{ 55 | return x; 56 | } 57 | 58 | int Element::getY() const{ 59 | return y; 60 | } 61 | 62 | int Element::getTotalX() const{ 63 | return x + parent->getTotalX(); 64 | } 65 | 66 | int Element::getTotalY() const{ 67 | return y + parent->getTotalY(); 68 | } 69 | 70 | Sprite* Element::getSprite(){ 71 | return parent->getSprite(); 72 | } 73 | 74 | void Element::setX(int x){ 75 | Element::x = x; 76 | } 77 | 78 | void Element::setY(int y){ 79 | Element::y = y; 80 | } 81 | 82 | void Element::setPos(int x, int y){ 83 | setX(x); 84 | setY(y); 85 | } 86 | 87 | void Element::clear(){ 88 | getSprite()->fillRect(getTotalX(), getTotalY(), getWidth(), getHeight(), background); 89 | } 90 | 91 | void Element::setParent(ElementContainer* parent){ 92 | Element::parent = parent; 93 | } 94 | -------------------------------------------------------------------------------- /src/UI/Element.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_ELEMENT_H 2 | #define CIRCUITOS_ELEMENT_H 3 | 4 | 5 | #include "../Display/Color.h" 6 | #include "../Display/Sprite.h" 7 | #include "ElementContainer.h" 8 | 9 | class ElementContainer; 10 | 11 | class Element { 12 | public: 13 | Element(); 14 | Element(ElementContainer* parent); 15 | virtual ~Element(); 16 | 17 | void setBorder(uint width, Color color); 18 | void setBorderColor(Color borderColor); 19 | void setBorderWidth(uint borderWidth); 20 | 21 | Color background = TFT_BLACK; 22 | Color borderColor; 23 | uint borderWidth = 0; 24 | 25 | Color chromaKey = TFT_TRANSPARENT; 26 | bool chroma = true; 27 | 28 | virtual uint getWidth() = 0; 29 | virtual uint getHeight() = 0; 30 | 31 | virtual void repos(); 32 | virtual void draw(); 33 | ElementContainer* getParent(); 34 | void setParent(ElementContainer* parent); 35 | 36 | virtual int getTotalX() const; 37 | virtual int getTotalY() const; 38 | virtual int getX() const; 39 | virtual int getY() const; 40 | virtual void setX(int x); 41 | virtual void setY(int y); 42 | virtual void setPos(int x, int y); 43 | 44 | virtual Sprite* getSprite(); 45 | 46 | virtual void clear(); 47 | 48 | private: 49 | ElementContainer* parent = nullptr; 50 | 51 | int x = 0; 52 | int y = 0; 53 | 54 | }; 55 | 56 | 57 | #endif //CIRCUITOS_ELEMENT_H 58 | -------------------------------------------------------------------------------- /src/UI/ElementContainer.cpp: -------------------------------------------------------------------------------- 1 | #include "ElementContainer.h" 2 | #include "../Util/Debug.h" 3 | 4 | ElementContainer& ElementContainer::addChild(Element* element){ 5 | children.push_back(element); 6 | return *this; 7 | } 8 | 9 | Element* ElementContainer::getChild(int i) const{ 10 | return children[i]; 11 | } 12 | 13 | void ElementContainer::repos(){ 14 | for(Element* element : children){ 15 | element->setPos(0, 0); 16 | element->repos(); 17 | } 18 | } 19 | 20 | void ElementContainer::draw(){ 21 | logln("Drawing container layout"); 22 | 23 | for(Element* el : children){ 24 | el->draw(); 25 | } 26 | 27 | Element::draw(); 28 | } 29 | 30 | Vector& ElementContainer::getChildren(){ 31 | return children; 32 | } 33 | 34 | ElementContainer::~ElementContainer(){ 35 | for(Element* el : children){ 36 | delete el; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/UI/ElementContainer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_ELEMENTCONTAINER_H 2 | #define CIRCUITOS_ELEMENTCONTAINER_H 3 | 4 | 5 | #include "../Util/Vector.h" 6 | #include "Element.h" 7 | 8 | class ElementContainer : public Element { 9 | public: 10 | using Element::Element; 11 | virtual ~ElementContainer(); 12 | 13 | virtual ElementContainer& addChild(Element* element); 14 | 15 | virtual uint getAvailableWidth() = 0; 16 | virtual uint getAvailableHeight() = 0; 17 | 18 | Element* getChild(int i) const; 19 | Vector& getChildren(); 20 | 21 | void repos() override; 22 | void draw() override; 23 | 24 | protected: 25 | Vector children; 26 | }; 27 | 28 | 29 | #endif //CIRCUITOS_ELEMENTCONTAINER_H 30 | -------------------------------------------------------------------------------- /src/UI/GridLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "GridLayout.h" 2 | #include "../Util/Debug.h" 3 | 4 | GridLayout::GridLayout(ElementContainer* parent, uint cols) : Layout(parent), cols(cols){ 5 | 6 | } 7 | 8 | void GridLayout::reflow(){ 9 | if(wType == PARENT){ 10 | setWidth(getParent()->getAvailableWidth()); 11 | }else if(wType == CHILDREN){ 12 | uint width = 2 * padding; 13 | 14 | if(!children.empty()){ 15 | uint maxRowWidth = 0; 16 | uint rowWidth = 0; 17 | 18 | uint i = 0; 19 | for(Element* el : children){ 20 | rowWidth += el->getWidth() + gutter; 21 | 22 | if(++i % cols == 0){ 23 | maxRowWidth = max(maxRowWidth, rowWidth - gutter); 24 | rowWidth = 0; 25 | } 26 | } 27 | 28 | if(rowWidth != 0){ 29 | maxRowWidth = max(maxRowWidth, rowWidth - gutter); 30 | } 31 | 32 | setWidth(width + maxRowWidth); 33 | } 34 | } 35 | 36 | if(hType == PARENT){ 37 | setHeight(getParent()->getAvailableHeight()); 38 | }else if(hType == CHILDREN && !children.empty()){ 39 | uint height = 2 * padding; 40 | uint rowHeight = 0; 41 | 42 | uint i = 0; 43 | for(Element* el : children){ 44 | rowHeight = max(rowHeight, el->getHeight()); 45 | 46 | if(++i % cols == 0){ 47 | height += rowHeight + gutter; 48 | rowHeight = 0; 49 | } 50 | } 51 | 52 | if(rowHeight == 0){ 53 | rowHeight -= gutter; 54 | } 55 | 56 | setHeight(height + rowHeight); 57 | } 58 | 59 | 60 | logln("Reflowing grid layout [" + String(getWidth()) + ", " + String(getHeight()) + "]"); 61 | logln("W/H Type " + String(wType) + ", " + String(hType) + " [ FIXED, CHILDREN, PARENT ]"); 62 | } 63 | 64 | void GridLayout::reposChildren(){ 65 | logln("Repositioning grid layout"); 66 | 67 | uint x = padding; 68 | uint y = padding; 69 | 70 | uint col = 0; 71 | uint maxHeight = 0; 72 | for(Element* el : children){ 73 | el->setPos(x, y); 74 | 75 | x += el->getWidth() + gutter; 76 | maxHeight = max(maxHeight, el->getHeight()); 77 | 78 | if(++col == cols){ 79 | y += maxHeight + gutter; 80 | x = padding; 81 | maxHeight = 0; 82 | col = 0; 83 | } 84 | } 85 | } 86 | 87 | void GridLayout::draw(){ 88 | if(!strictPos){ 89 | Layout::draw(); 90 | return; 91 | } 92 | 93 | uint x = padding; 94 | uint y = padding; 95 | 96 | uint col = 0; 97 | uint maxHeight = 0; 98 | for(Element* el : children){ 99 | el->setPos(x, y); 100 | logln("GL Setting pos [" + String(x) + ", " + String(y) + "]"); 101 | el->draw(); 102 | 103 | x += el->getWidth() + gutter; 104 | maxHeight = max(maxHeight, el->getHeight()); 105 | 106 | if(++col == cols){ 107 | y += maxHeight + gutter; 108 | x = padding; 109 | maxHeight = 0; 110 | col = 0; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/UI/GridLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_GridLayout_H 2 | #define CIRCUITOS_GridLayout_H 3 | 4 | #include "Layout.h" 5 | 6 | class GridLayout : public Layout { 7 | public: 8 | GridLayout(ElementContainer* parent, uint cols); 9 | 10 | void reposChildren() override; 11 | void reflow() override; 12 | void draw() override; 13 | 14 | private: 15 | 16 | uint cols; 17 | 18 | }; 19 | 20 | 21 | #endif //CIRCUITOS_GridLayout_H 22 | -------------------------------------------------------------------------------- /src/UI/Image.cpp: -------------------------------------------------------------------------------- 1 | #include "Image.h" 2 | #include "../Util/Debug.h" 3 | 4 | Image::Image(ElementContainer* parent, uint width, uint height) : width(width), height(height), 5 | Element(parent), SpriteElement(parent->getSprite(), width, height){ 6 | 7 | } 8 | 9 | void Image::draw(){ 10 | sprite.setParent(getParent()->getSprite()); 11 | sprite.setPos(getTotalX(), getTotalY()); 12 | sprite.push(); 13 | Element::draw(); 14 | } 15 | 16 | uint Image::getWidth(){ 17 | return width; 18 | } 19 | 20 | uint Image::getHeight(){ 21 | return height; 22 | } 23 | 24 | Sprite* Image::getSprite(){ 25 | return SpriteElement::getSprite(); 26 | } 27 | 28 | void Image::setWidth(uint width){ 29 | Image::width = width; 30 | } 31 | 32 | void Image::setHeight(uint height){ 33 | Image::height = height; 34 | } 35 | 36 | void Image::setSize(uint width, uint height){ 37 | setWidth(width); 38 | setHeight(height); 39 | } 40 | -------------------------------------------------------------------------------- /src/UI/Image.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_IMAGE_H 2 | #define CIRCUITOS_IMAGE_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | #include "SpriteElement.h" 7 | 8 | class Image : public Element, public SpriteElement { 9 | public: 10 | Image(ElementContainer* parent, uint width, uint height); 11 | 12 | Sprite* getSprite() override; 13 | void draw() override; 14 | 15 | uint getWidth() override; 16 | uint getHeight() override; 17 | 18 | void setWidth(uint width); 19 | void setHeight(uint height); 20 | void setSize(uint width, uint height); 21 | 22 | private: 23 | uint width; 24 | uint height; 25 | 26 | }; 27 | 28 | 29 | #endif //CIRCUITOS_IMAGE_H 30 | -------------------------------------------------------------------------------- /src/UI/Layout.cpp: -------------------------------------------------------------------------------- 1 | #include "Layout.h" 2 | #include "../Util/Debug.h" 3 | 4 | void Layout::reflow(){ 5 | if(wType == CHILDREN){ 6 | uint width = 0; 7 | 8 | for(Element* child : children){ 9 | width = max(width, child->getWidth()); 10 | } 11 | 12 | setWidth(width); 13 | }else if(wType == PARENT){ 14 | setWidth(getParent()->getAvailableWidth()); 15 | } 16 | 17 | if(hType == CHILDREN){ 18 | uint height = 0; 19 | 20 | for(Element* child : children){ 21 | height = max(height, child->getHeight()); 22 | } 23 | 24 | setHeight(height); 25 | }else if(hType == PARENT){ 26 | setHeight(getParent()->getAvailableHeight()); 27 | } 28 | 29 | logln("Reflowing layout [" + String(getWidth()) + ", " + String(getHeight()) + "]"); 30 | logln("W/H Type " + String(wType) + ", " + String(hType) + " [ FIXED, CHILDREN, PARENT ]"); 31 | } 32 | 33 | void Layout::repos(){ 34 | reposChildren(); 35 | 36 | for(Element* element : children){ 37 | element->repos(); 38 | } 39 | } 40 | 41 | void Layout::reposChildren(){ 42 | 43 | } 44 | 45 | void Layout::draw(){ 46 | logln("Drawing layout"); 47 | 48 | if(strictPos){ 49 | reposChildren(); 50 | } 51 | 52 | ElementContainer::draw(); 53 | } 54 | 55 | Layout& Layout::setPadding(uint padding){ 56 | Layout::padding = padding; 57 | return *this; 58 | } 59 | 60 | Layout& Layout::setGutter(uint gutter){ 61 | Layout::gutter = gutter; 62 | return *this; 63 | } 64 | 65 | void Layout::setWidth(uint width){ 66 | Layout::width = width; 67 | } 68 | 69 | void Layout::setHeight(uint height){ 70 | Layout::height = height; 71 | } 72 | 73 | WHType Layout::getHType() const{ 74 | return hType; 75 | } 76 | 77 | WHType Layout::getWType() const{ 78 | return wType; 79 | } 80 | 81 | void Layout::setWHType(WHType wType, WHType hType){ 82 | setWType(wType); 83 | setHType(hType); 84 | } 85 | 86 | void Layout::setWType(WHType wType){ 87 | Layout::wType = wType; 88 | } 89 | 90 | void Layout::setHType(WHType hType){ 91 | Layout::hType = hType; 92 | } 93 | 94 | uint Layout::getAvailableWidth(){ 95 | return width - 2 * padding; 96 | } 97 | 98 | uint Layout::getAvailableHeight(){ 99 | return height - 2 * padding; 100 | } 101 | 102 | uint Layout::getWidth(){ 103 | return width; 104 | } 105 | 106 | uint Layout::getHeight(){ 107 | return height; 108 | } 109 | 110 | uint Layout::getGutter() const{ 111 | return gutter; 112 | } 113 | 114 | uint Layout::getPadding() const{ 115 | return padding; 116 | } 117 | 118 | void Layout::setStrictPos(bool strictPos){ 119 | this->strictPos = strictPos; 120 | } 121 | -------------------------------------------------------------------------------- /src/UI/Layout.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_LAYOUT_H 2 | #define CIRCUITOS_LAYOUT_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | #include "Element.h" 7 | 8 | enum WHType { FIXED, CHILDREN, PARENT }; 9 | 10 | class Layout : public ElementContainer { 11 | public: 12 | using ElementContainer::ElementContainer; 13 | 14 | virtual void reflow(); 15 | virtual void repos() override; 16 | 17 | void draw() override; 18 | 19 | Layout& setPadding(uint padding); 20 | Layout& setGutter(uint gutter); 21 | 22 | uint getGutter() const; 23 | uint getPadding() const; 24 | 25 | void setWHType(WHType wType, WHType hType); 26 | void setWType(WHType wType); 27 | void setHType(WHType hType); 28 | WHType getWType() const; 29 | WHType getHType() const; 30 | 31 | void setWidth(uint width); 32 | void setHeight(uint height); 33 | uint getAvailableWidth() override; 34 | uint getAvailableHeight() override; 35 | uint getWidth() override; 36 | uint getHeight() override; 37 | 38 | void setStrictPos(bool strictPos); 39 | 40 | protected: 41 | uint gutter = 0; 42 | uint padding = 0; 43 | 44 | WHType wType = FIXED; 45 | WHType hType = FIXED; 46 | 47 | bool strictPos = false; 48 | 49 | private: 50 | uint width = 0; 51 | uint height = 0; 52 | 53 | virtual void reposChildren(); 54 | }; 55 | 56 | 57 | #endif //CIRCUITOS_LAYOUT_H 58 | -------------------------------------------------------------------------------- /src/UI/LinearLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "LinearLayout.h" 2 | #include "../Util/Debug.h" 3 | 4 | LinearLayout::LinearLayout(ElementContainer* parent, LayoutDirection direction) : Layout(parent), direction(direction){ 5 | 6 | } 7 | 8 | void LinearLayout::reposChildren(){ 9 | logln("Repositioning linear layout"); 10 | 11 | int x = padding; 12 | int y = padding; 13 | 14 | for(Element* el : children){ 15 | el->setPos(x, y); 16 | 17 | if(direction == VERTICAL){ 18 | y += gutter + el->getHeight(); 19 | }else if(direction == HORIZONTAL){ 20 | x += gutter + el->getWidth(); 21 | } 22 | } 23 | } 24 | 25 | void LinearLayout::draw(){ 26 | logln("Drawing linear layout"); 27 | 28 | if(!strictPos){ 29 | Layout::draw(); 30 | return; 31 | } 32 | 33 | int x = padding; 34 | int y = padding; 35 | 36 | for(Element* el : children){ 37 | el->setPos(x, y); 38 | logln("LL Setting pos [" + String(x) + ", " + String(y) + "]"); 39 | el->draw(); 40 | 41 | if(direction == VERTICAL){ 42 | y += gutter + el->getHeight(); 43 | }else if(direction == HORIZONTAL){ 44 | x += gutter + el->getWidth(); 45 | } 46 | } 47 | 48 | Element::draw(); 49 | } 50 | 51 | void LinearLayout::reflow(){ 52 | if(direction == HORIZONTAL){ 53 | reflowHorizontal(); 54 | }else if(direction == VERTICAL){ 55 | reflowVertical(); 56 | } 57 | 58 | logln("Reflowing linear layout [" + String(getWidth()) + ", " + String(getHeight()) + "]"); 59 | logln("W/H Type " + String(wType) + ", " + String(hType) + " [ FIXED, CHILDREN, PARENT ]"); 60 | } 61 | 62 | void LinearLayout::reflowHorizontal(){ 63 | if(wType == PARENT){ 64 | setWidth(getParent()->getAvailableWidth()); 65 | }else if(wType == CHILDREN){ 66 | uint width = 2 * padding; 67 | 68 | if(!children.empty()){ 69 | for(Element* el : children){ 70 | width += el->getWidth() + gutter; 71 | } 72 | 73 | width -= gutter; 74 | } 75 | 76 | setWidth(width); 77 | } 78 | 79 | if(hType == PARENT){ 80 | setHeight(getParent()->getAvailableHeight()); 81 | }else if(hType == CHILDREN){ 82 | uint maxHeight = 0; 83 | 84 | for(Element* el : children){ 85 | maxHeight = max(maxHeight, el->getHeight()); 86 | } 87 | 88 | setHeight(maxHeight + 2 * padding); 89 | } 90 | } 91 | 92 | void LinearLayout::reflowVertical(){ 93 | if(wType == PARENT){ 94 | setWidth(getParent()->getAvailableWidth()); 95 | }else if(wType == CHILDREN){ 96 | uint maxWidth = 0; 97 | 98 | for(Element* el : children){ 99 | maxWidth = max(maxWidth, el->getWidth()); 100 | } 101 | 102 | setWidth(maxWidth + 2 * padding); 103 | } 104 | 105 | if(hType == PARENT){ 106 | setHeight(getParent()->getAvailableHeight()); 107 | }else if(hType == CHILDREN){ 108 | uint height = 2 * padding; 109 | 110 | if(!children.empty()){ 111 | for(Element* el : children){ 112 | height += el->getHeight() + gutter; 113 | } 114 | 115 | height -= gutter; 116 | } 117 | 118 | setHeight(height); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/UI/LinearLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_LINEARLAYOUT_H 2 | #define CIRCUITOS_LINEARLAYOUT_H 3 | 4 | #include "Layout.h" 5 | 6 | enum LayoutDirection { HORIZONTAL, VERTICAL }; 7 | 8 | class LinearLayout : public Layout { 9 | public: 10 | LinearLayout(ElementContainer* parent, LayoutDirection direction); 11 | 12 | void reflow() override; 13 | void reposChildren() override; 14 | 15 | void draw() override; 16 | 17 | private: 18 | 19 | LayoutDirection direction; 20 | 21 | void reflowHorizontal(); 22 | void reflowVertical(); 23 | }; 24 | 25 | 26 | #endif //CIRCUITOS_LINEARLAYOUT_H 27 | -------------------------------------------------------------------------------- /src/UI/LowRamScreen.impl: -------------------------------------------------------------------------------- 1 | #include "Screen.h" 2 | #include "../Util/Debug.h" 3 | 4 | Screen::Screen(Display& display) : Screen(display, display.getWidth(), display.getHeight()){ 5 | 6 | } 7 | 8 | Screen::Screen(Display& display, uint width, uint height) : display(&display), width(width), height(height), SpriteElement(display.getBaseSprite(), width, height){ 9 | sprite.cleanup(); 10 | } 11 | 12 | Screen& Screen::addChild(Element* element){ 13 | if(element == nullptr){ 14 | children.clear(); 15 | }else if(children.empty()){ 16 | children.push_back(element); 17 | }else{ 18 | children[0] = element; 19 | } 20 | } 21 | 22 | void Screen::draw(){ 23 | logln("Drawing screen"); 24 | ElementContainer::draw(); 25 | } 26 | 27 | void Screen::commit(){ 28 | display->commit(); 29 | } 30 | 31 | uint Screen::getAvailableWidth(){ 32 | return getWidth(); 33 | } 34 | 35 | uint Screen::getAvailableHeight(){ 36 | return getHeight(); 37 | } 38 | 39 | uint Screen::getWidth(){ 40 | return width; 41 | } 42 | 43 | uint Screen::getHeight(){ 44 | return height; 45 | } 46 | 47 | Display* Screen::getDisplay() const{ 48 | return display; 49 | } 50 | 51 | int Screen::getTotalX() const{ 52 | return getX(); // getX(); 53 | } 54 | 55 | int Screen::getTotalY() const{ 56 | return getY(); // getY(); 57 | } 58 | 59 | void Screen::setPos(int x, int y){ 60 | setX(x); 61 | setY(y); 62 | } 63 | 64 | Sprite* Screen::getSprite(){ 65 | return display->getBaseSprite(); 66 | } 67 | 68 | void Screen::pack() 69 | { 70 | } 71 | 72 | void Screen::unpack() 73 | { 74 | } 75 | -------------------------------------------------------------------------------- /src/UI/Screen.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_LOWRAM 4 | #include "LowRamScreen.impl" 5 | #else 6 | #include "StandardScreen.impl" 7 | #endif -------------------------------------------------------------------------------- /src/UI/Screen.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SCREEN_H 2 | #define CIRCUITOS_SCREEN_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | #include "Element.h" 7 | #include "SpriteElement.h" 8 | #include "../Setup.hpp" 9 | 10 | class Screen : public ElementContainer, public SpriteElement { 11 | public: 12 | explicit Screen(Display& display); 13 | Screen(Display& display, uint width, uint height); 14 | 15 | void pack() override; 16 | void unpack() override; 17 | 18 | Sprite* getSprite() override; 19 | 20 | void draw() override; 21 | 22 | Screen& addChild(Element* element) override; 23 | 24 | uint getAvailableWidth() override; 25 | uint getAvailableHeight() override; 26 | uint getWidth() override; 27 | uint getHeight() override; 28 | 29 | Display* getDisplay() const; 30 | 31 | #ifndef CIRCUITOS_LOWRAM 32 | int getX() const override; 33 | int getY() const override; 34 | #endif 35 | 36 | int getTotalX() const override; 37 | int getTotalY() const override; 38 | 39 | void setPos(int x, int y) override; 40 | 41 | void commit(); 42 | 43 | private: 44 | Display* display; 45 | 46 | uint width; 47 | uint height; 48 | 49 | /** Hiding */ 50 | #ifndef CIRCUITOS_LOWRAM 51 | using Element::setX; 52 | using Element::setY; 53 | #endif 54 | }; 55 | 56 | 57 | #endif //CIRCUITOS_SCREEN_H 58 | -------------------------------------------------------------------------------- /src/UI/ScrollLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "ScrollLayout.h" 2 | #include "../Util/Debug.h" 3 | 4 | ScrollLayout::ScrollLayout(ElementContainer* parent) : Layout(parent){ 5 | 6 | } 7 | 8 | void ScrollLayout::reposChildren(){ 9 | logln("Repositioning scroll layout"); 10 | 11 | if(!children.empty()){ 12 | children[0]->setPos(padding - scrollX, padding - scrollY); 13 | } 14 | } 15 | 16 | void ScrollLayout::draw(){ 17 | if(!strictPos){ 18 | Layout::draw(); 19 | return; 20 | } 21 | 22 | if(!children.empty()){ 23 | children[0]->setPos(padding - scrollX, padding - scrollY); 24 | children[0]->draw(); 25 | } 26 | 27 | Element::draw(); 28 | } 29 | 30 | ElementContainer& ScrollLayout::addChild(Element* element){ 31 | if(element == nullptr){ 32 | children.clear(); 33 | }else if(children.empty()){ 34 | children.push_back(element); 35 | }else{ 36 | children[0] = element; 37 | } 38 | } 39 | 40 | void ScrollLayout::setScroll(uint scrollX, uint scrollY){ 41 | ScrollLayout::scrollX = scrollX; 42 | ScrollLayout::scrollY = scrollY; 43 | 44 | if(children.empty()) return; 45 | 46 | children[0]->setPos(padding - scrollX, padding - scrollY); 47 | } 48 | 49 | uint ScrollLayout::getMaxScrollX(){ 50 | if(children.empty()) return 0; 51 | 52 | // child element is visible on the x axis - no scroll 53 | if(children[0]->getWidth() < getWidth()){ 54 | return 0; 55 | } 56 | 57 | return children[0]->getWidth() - getWidth(); 58 | } 59 | 60 | uint ScrollLayout::getMaxScrollY(){ 61 | if(children.empty()) return 0; 62 | 63 | // child element is visible on the y axis - no scroll 64 | if(children[0]->getHeight() < getHeight()){ 65 | return 0; 66 | } 67 | 68 | return children[0]->getHeight() - getHeight(); 69 | } 70 | 71 | uint ScrollLayout::getScrollX() const{ 72 | return scrollX; 73 | } 74 | 75 | uint ScrollLayout::getScrollY() const{ 76 | return scrollY; 77 | } 78 | 79 | void ScrollLayout::scrollIntoView(uint child, int over){ 80 | if(children.empty()) return; 81 | 82 | ElementContainer* container = reinterpret_cast(children[0]); 83 | if(child >= container->getChildren().size()) return; 84 | Element* childElement = container->getChild(child); 85 | 86 | int scrollX = this->scrollX; 87 | int scrollY = this->scrollY; 88 | 89 | if(childElement->getX() < getScrollX()){ 90 | scrollX = (int) childElement->getX() - over; 91 | }else if((childElement->getX() + childElement->getWidth() + over) > (getScrollX() + getWidth())){ 92 | scrollX = (int) childElement->getX() + childElement->getWidth() + over - getWidth(); 93 | } 94 | 95 | if(childElement->getY() < getScrollY()){ 96 | scrollY = (int) childElement->getY() - over; 97 | }else if((childElement->getY() + childElement->getHeight() + over) > (getScrollY() + getHeight())){ 98 | scrollY = (int) childElement->getY() + childElement->getHeight() + over - getHeight(); 99 | } 100 | 101 | setScroll(max(0, min(scrollX, (int) getMaxScrollX())), max(0, min(scrollY, (int) getMaxScrollY()))); 102 | } 103 | -------------------------------------------------------------------------------- /src/UI/ScrollLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SCROLLLAYOUT_H 2 | #define CIRCUITOS_SCROLLLAYOUT_H 3 | 4 | 5 | #include "Layout.h" 6 | 7 | class ScrollLayout : public Layout { 8 | public: 9 | ScrollLayout(ElementContainer* parent); 10 | ElementContainer& addChild(Element* element) override; 11 | 12 | void setScroll(uint scrollX, uint scrollY); 13 | 14 | uint getMaxScrollX(); 15 | uint getMaxScrollY(); 16 | 17 | uint getScrollX() const; 18 | uint getScrollY() const; 19 | 20 | /** 21 | * Scroll a child of the containing element into view. Causes unexpected behaviour if the child of this element 22 | * isn't an ElementContainer. 23 | * @param child 24 | * @param over How much to scroll past the element 25 | */ 26 | void scrollIntoView(uint child, int over = 0); 27 | 28 | void reposChildren() override; 29 | void draw() override; 30 | 31 | private: 32 | 33 | uint scrollX = 0; 34 | uint scrollY = 0; 35 | }; 36 | 37 | 38 | #endif //CIRCUITOS_SCROLLLAYOUT_H 39 | -------------------------------------------------------------------------------- /src/UI/SpriteElement.cpp: -------------------------------------------------------------------------------- 1 | #include "SpriteElement.h" 2 | 3 | SpriteElement::SpriteElement(Sprite* parentSprite, uint width, uint height) : sprite(parentSprite, width, height){ 4 | 5 | } 6 | 7 | SpriteElement::~SpriteElement(){ } 8 | 9 | Sprite* SpriteElement::getSprite(){ 10 | return &sprite; 11 | } 12 | 13 | void SpriteElement::pack(){ 14 | sprite.cleanup(); 15 | } 16 | 17 | void SpriteElement::unpack(){ 18 | sprite.resize(getWidth(), getHeight()); 19 | } 20 | -------------------------------------------------------------------------------- /src/UI/SpriteElement.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SPRITEELEMENT_H 2 | #define CIRCUITOS_SPRITEELEMENT_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | 7 | class SpriteElement { 8 | public: 9 | SpriteElement(Sprite* parentSprite, uint width, uint height); 10 | virtual ~SpriteElement(); 11 | 12 | virtual void pack(); 13 | virtual void unpack(); 14 | 15 | virtual uint getWidth() = 0; 16 | virtual uint getHeight() = 0; 17 | 18 | virtual Sprite* getSprite(); 19 | 20 | protected: 21 | Sprite sprite; 22 | }; 23 | 24 | 25 | #endif //CIRCUITOS_SPRITEELEMENT_H 26 | -------------------------------------------------------------------------------- /src/UI/StandardScreen.impl: -------------------------------------------------------------------------------- 1 | #include "Screen.h" 2 | #include "../Util/Debug.h" 3 | 4 | Screen::Screen(Display& display) : Screen(display, display.getWidth(), display.getHeight()){ 5 | 6 | } 7 | 8 | Screen::Screen(Display& display, uint width, uint height) : display(&display), SpriteElement(display.getBaseSprite(), width, height), width(width), height(height){ 9 | sprite.setPos(0, 0); 10 | sprite.clear(TFT_BLACK); 11 | } 12 | 13 | Screen& Screen::addChild(Element* element){ 14 | if(element == nullptr){ 15 | children.clear(); 16 | }else if(children.empty()){ 17 | children.push_back(element); 18 | }else{ 19 | children[0] = element; 20 | } 21 | } 22 | 23 | void Screen::draw(){ 24 | logln("Drawing screen"); 25 | 26 | ElementContainer::draw(); 27 | } 28 | 29 | void Screen::commit(){ 30 | sprite.push(); 31 | display->commit(); 32 | } 33 | 34 | uint Screen::getAvailableWidth(){ 35 | return getWidth(); 36 | } 37 | 38 | uint Screen::getAvailableHeight(){ 39 | return getHeight(); 40 | } 41 | 42 | uint Screen::getWidth(){ 43 | return width; 44 | } 45 | 46 | uint Screen::getHeight(){ 47 | return height; 48 | } 49 | 50 | Display* Screen::getDisplay() const{ 51 | return display; 52 | } 53 | 54 | int Screen::getX() const{ 55 | return sprite.getX(); 56 | } 57 | 58 | int Screen::getY() const{ 59 | return sprite.getY(); 60 | } 61 | 62 | int Screen::getTotalX() const{ 63 | return 0; // getX(); 64 | } 65 | 66 | int Screen::getTotalY() const{ 67 | return 0; // getY(); 68 | } 69 | 70 | void Screen::setPos(int x, int y){ 71 | sprite.setPos(x, y); 72 | } 73 | 74 | Sprite* Screen::getSprite(){ 75 | return SpriteElement::getSprite(); 76 | } 77 | 78 | void Screen::pack() 79 | { 80 | sprite.cleanup(); 81 | } 82 | 83 | void Screen::unpack() 84 | { 85 | sprite.resize(getWidth(), getHeight()); 86 | } -------------------------------------------------------------------------------- /src/UI/TextElement.cpp: -------------------------------------------------------------------------------- 1 | #include "TextElement.h" 2 | 3 | TextElement::TextElement(ElementContainer* parent, uint width, uint height) : 4 | Element(parent), width(width), height(height){ 5 | 6 | } 7 | 8 | TextElement& TextElement::setFont(uint font){ 9 | TextElement::textFont = font; 10 | return *this; 11 | } 12 | 13 | TextElement& TextElement::setSize(uint size){ 14 | TextElement::textSize = size; 15 | return *this; 16 | } 17 | 18 | TextElement& TextElement::setColor(Color color){ 19 | TextElement::textColor = color; 20 | return *this; 21 | } 22 | 23 | void TextElement::setAlignment(TextElement::TextAlignment alignment){ 24 | this->textAlignment = alignment; 25 | } 26 | 27 | void TextElement::draw(){ 28 | Sprite* canvas = getSprite(); 29 | canvas->setTextFont(textFont); 30 | canvas->setTextColor(textColor); 31 | canvas->setTextSize(textSize); 32 | 33 | uint textWidth = canvas->textWidth(text.c_str()); 34 | uint textHeight = canvas->fontHeight(); 35 | 36 | uint textX; 37 | switch(textAlignment){ 38 | default: 39 | case LEFT: 40 | textX = 0; 41 | break; 42 | case CENTER: 43 | textX = (width - textWidth) / 2; 44 | break; 45 | case RIGHT: 46 | textX = width - textWidth; 47 | break; 48 | } 49 | 50 | canvas->setCursor(getTotalX() + textX, getTotalY() + (height - textHeight) / 2 + 1); 51 | canvas->print(text.c_str()); 52 | Element::draw(); 53 | } 54 | 55 | const std::string& TextElement::getText() const{ 56 | return text; 57 | } 58 | 59 | void TextElement::setText(const std::string& text){ 60 | TextElement::text = text; 61 | } 62 | 63 | uint TextElement::getWidth(){ 64 | return width; 65 | } 66 | 67 | uint TextElement::getHeight(){ 68 | return height; 69 | } 70 | 71 | void TextElement::setWidth(uint width){ 72 | TextElement::width = width; 73 | } 74 | 75 | void TextElement::setHeight(uint height){ 76 | TextElement::height = height; 77 | } 78 | -------------------------------------------------------------------------------- /src/UI/TextElement.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_TEXTELEMENT_H 2 | #define CIRCUITOS_TEXTELEMENT_H 3 | 4 | 5 | #include "ElementContainer.h" 6 | #include "../Display/Sprite.h" 7 | #include 8 | 9 | class TextElement : public Element { 10 | public: 11 | TextElement(ElementContainer* parent, uint width, uint height); 12 | 13 | TextElement& setFont(uint font); 14 | TextElement& setSize(uint fontSize); 15 | TextElement& setColor(Color fontColor); 16 | 17 | void draw() override; 18 | 19 | const std::string& getText() const; 20 | void setText(const std::string& text); 21 | 22 | uint getWidth() override; 23 | uint getHeight() override; 24 | 25 | void setWidth(uint width); 26 | void setHeight(uint height); 27 | 28 | enum TextAlignment { LEFT, CENTER, RIGHT }; 29 | void setAlignment(TextAlignment alignment); 30 | 31 | private: 32 | uint width; 33 | uint height; 34 | 35 | uint textFont = 0; 36 | uint textSize = 1; 37 | Color textColor = TFT_DARKGREY; 38 | TextAlignment textAlignment = LEFT; 39 | 40 | std::string text = ""; 41 | 42 | }; 43 | 44 | 45 | #endif //CIRCUITOS_TEXTELEMENT_H 46 | -------------------------------------------------------------------------------- /src/Util/Debug.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug.h" 2 | 3 | void Debug::println(String msg){ 4 | #ifdef DEBUG 5 | Serial.println(msg); 6 | #endif 7 | } 8 | 9 | void Debug::println(){ 10 | #ifdef DEBUG 11 | Serial.println(); 12 | #endif 13 | } 14 | 15 | void Debug::print(String msg){ 16 | #ifdef DEBUG 17 | Serial.print(msg); 18 | #endif 19 | } 20 | -------------------------------------------------------------------------------- /src/Util/Debug.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_DEBUG_H 2 | #define CIRCUITOS_DEBUG_H 3 | 4 | #include 5 | 6 | // should be set with compiler parameters 7 | #define NDEBUG 8 | 9 | #ifdef DEBUG 10 | #define logln(msg) Debug::println(msg) 11 | #define logr(msg) Debug::print(msg) 12 | #else 13 | #define logln(msg) 14 | #define logr(msg) 15 | #endif 16 | 17 | class Debug { 18 | public: 19 | static void println(String msg); 20 | static void println(); 21 | static void print(String msg); 22 | 23 | }; 24 | 25 | #endif //CIRCUITOS_DEBUG_H 26 | -------------------------------------------------------------------------------- /src/Util/GIF.cpp: -------------------------------------------------------------------------------- 1 | #include "GIF.h" 2 | #include 3 | 4 | GIF::GIF(){ } 5 | 6 | GIF::GIF(fs::File file){ 7 | if(!file) return; 8 | file.seek(0); 9 | gif = CircuitOS::gd_open_gif(file); 10 | data = std::shared_ptr(new Pixel[getWidth()*getHeight()], std::default_delete()); 11 | } 12 | 13 | GIF::~GIF(){ 14 | if(gif == nullptr) return; 15 | gd_close_gif(gif); 16 | } 17 | 18 | GIF::operator bool() const{ 19 | return gif != nullptr; 20 | } 21 | 22 | GIF& GIF::operator=(const GIF& other){ 23 | if(this == &other) return *this; 24 | 25 | if(gif){ 26 | gd_close_gif(gif); 27 | gif = nullptr; 28 | } 29 | 30 | loopMode = other.loopMode; 31 | loopCount = 0; 32 | 33 | File file = other.gif->fd; 34 | file.seek(0); 35 | gif = CircuitOS::gd_open_gif(file); 36 | 37 | return *this; 38 | } 39 | 40 | bool GIF::nextFrame(){ 41 | if(gif == nullptr) return false; 42 | 43 | if(gd_get_frame(gif) != 1){ 44 | loopCount++; 45 | 46 | switch(loopMode){ 47 | case Auto: 48 | if(gif->loop_count != 0 && loopCount == gif->loop_count) return false; 49 | else{ 50 | gd_rewind(gif); 51 | gd_get_frame(gif); 52 | } 53 | break; 54 | case Single: 55 | return false; 56 | case Infinite: 57 | gd_rewind(gif); 58 | gd_get_frame(gif); 59 | break; 60 | } 61 | } 62 | 63 | return true; 64 | } 65 | 66 | GIF::Frame GIF::getFrame() const{ 67 | if(gif == nullptr) return { }; 68 | 69 | 70 | gd_render_frame(gif, reinterpret_cast(data.get()), false); 71 | 72 | return { getWidth(), getHeight(), (uint32_t) gif->gce.delay * 10, data }; 73 | } 74 | 75 | uint32_t GIF::frameDuration() const{ 76 | if(gif == nullptr) return 0; 77 | return gif->gce.delay * 10; 78 | } 79 | 80 | void GIF::reset(){ 81 | if(gif == nullptr) return; 82 | gd_rewind(gif); 83 | loopCount = 0; 84 | } 85 | 86 | uint16_t GIF::getWidth() const{ 87 | return gif->width; 88 | } 89 | 90 | uint16_t GIF::getHeight() const{ 91 | return gif->height; 92 | } 93 | 94 | GIF::LoopMode GIF::getLoopMode() const{ 95 | return loopMode; 96 | } 97 | 98 | void GIF::setLoopMode(GIF::LoopMode loopMode){ 99 | GIF::loopMode = loopMode; 100 | } 101 | 102 | uint32_t GIF::getLoopCount() const{ 103 | return loopCount; 104 | } 105 | 106 | GIF::Frame::Frame(){ } 107 | 108 | GIF::Frame::Frame(uint16_t width, uint16_t height, uint32_t duration, std::shared_ptr data) : width(width), height(height), duration(duration), data(data){ } 109 | 110 | GIF::Frame::Frame(const GIF::Frame& other) : Frame(other.width, other.height, other.duration, other.data){ 111 | *this = other; 112 | } 113 | 114 | GIF::Frame& GIF::Frame::operator=(const GIF::Frame& other){ 115 | if(&other == this) return *this; 116 | 117 | width = other.width; 118 | height = other.height; 119 | duration = other.duration; 120 | 121 | data.reset(); 122 | data = other.data; 123 | 124 | return *this; 125 | } 126 | 127 | uint16_t GIF::Frame::getWidth() const{ 128 | return width; 129 | } 130 | 131 | uint16_t GIF::Frame::getHeight() const{ 132 | return height; 133 | } 134 | 135 | uint32_t GIF::Frame::getDuration() const{ 136 | return duration; 137 | } 138 | 139 | const Pixel* GIF::Frame::getData() const{ 140 | return data.get(); 141 | } 142 | -------------------------------------------------------------------------------- /src/Util/GIF.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_GIF_H 2 | #define CIRCUITOS_GIF_H 3 | 4 | #include 5 | #include 6 | #include "gifdec.h" 7 | #include "../Display/Color.h" 8 | 9 | class GIF { 10 | public: 11 | GIF(); 12 | GIF(fs::File file); 13 | virtual ~GIF(); 14 | 15 | operator bool() const; 16 | GIF& operator=(const GIF& other); 17 | 18 | uint16_t getWidth() const; 19 | uint16_t getHeight() const; 20 | 21 | enum LoopMode { Auto, Single, Infinite }; 22 | LoopMode getLoopMode() const; 23 | void setLoopMode(LoopMode loopMode); 24 | 25 | struct Frame { 26 | public: 27 | Frame(); 28 | Frame(uint16_t width, uint16_t height, uint32_t duration, std::shared_ptr data); 29 | Frame(const Frame& other); 30 | Frame& operator=(const Frame& other); 31 | uint16_t getWidth() const; 32 | uint16_t getHeight() const; 33 | uint32_t getDuration() const; 34 | const Pixel* getData() const; 35 | 36 | private: 37 | uint16_t width = 0, height = 0; 38 | uint32_t duration = 0; // [ms] 39 | std::shared_ptr data; 40 | 41 | }; 42 | 43 | void reset(); 44 | bool nextFrame(); 45 | Frame getFrame() const; 46 | uint32_t frameDuration() const; 47 | uint32_t getLoopCount() const; 48 | 49 | private: 50 | CircuitOS::gd_GIF* gif = nullptr; 51 | 52 | std::shared_ptr data; 53 | 54 | LoopMode loopMode = Auto; 55 | uint32_t loopCount = 0; 56 | 57 | }; 58 | 59 | 60 | #endif //CIRCUITOS_GIF_H 61 | -------------------------------------------------------------------------------- /src/Util/HWRevision.cpp: -------------------------------------------------------------------------------- 1 | #include "HWRevision.h" 2 | #include 3 | #include 4 | #include 5 | 6 | bool HWRevision::changes = false; 7 | 8 | uint8_t HWRevision::get(){ 9 | return REG_GET_FIELD(EFUSE_BLK3_RDATA0_REG, EFUSE_ADC1_TP_LOW); 10 | } 11 | 12 | void HWRevision::write(const uint8_t value){ 13 | const auto trimmed = value & 0b01111111; 14 | if(trimmed != value){ 15 | ESP_LOGE("HWRevision", "Trimmed value doesn't match provided."); 16 | return; 17 | } 18 | 19 | REG_SET_FIELD(EFUSE_BLK3_WDATA0_REG, EFUSE_ADC1_TP_LOW, trimmed); 20 | changes = true; 21 | } 22 | 23 | void HWRevision::commit(){ 24 | if(!changes){ 25 | ESP_LOGE("HWRevision", "No changes to commit."); 26 | return; 27 | } 28 | 29 | esp_efuse_burn_new_values(); 30 | esp_efuse_reset(); 31 | changes = false; 32 | } 33 | 34 | void HWRevision::reset(){ 35 | esp_efuse_reset(); 36 | changes = false; 37 | } 38 | -------------------------------------------------------------------------------- /src/Util/HWRevision.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_HWREVISION_H 2 | #define CIRCUITOS_HWREVISION_H 3 | 4 | #include 5 | 6 | class HWRevision { 7 | public: 8 | static uint8_t get(); 9 | 10 | static void write(uint8_t value); 11 | 12 | static void commit(); 13 | static void reset(); 14 | 15 | private: 16 | static bool changes; 17 | 18 | }; 19 | 20 | 21 | #endif //CIRCUITOS_HWREVISION_H 22 | -------------------------------------------------------------------------------- /src/Util/NullStream.cpp: -------------------------------------------------------------------------------- 1 | #include "NullStream.h" 2 | 3 | NullStream::NullStream(){ 4 | setTimeout(0); // no timeout. 5 | } 6 | 7 | int NullStream::available(){ 8 | return 0; 9 | } 10 | 11 | int NullStream::peek(){ 12 | return EOF; 13 | } 14 | 15 | int NullStream::read(){ 16 | return EOF; 17 | } 18 | 19 | void NullStream::flush(){ 20 | return; 21 | } 22 | 23 | size_t NullStream::write(const uint8_t data){ 24 | bottomlessPit = data; 25 | return 1; 26 | } 27 | 28 | size_t NullStream::write(const uint8_t* buffer, size_t size){ 29 | if(size > 0) bottomlessPit = buffer[size - 1]; 30 | return size; 31 | } 32 | -------------------------------------------------------------------------------- /src/Util/NullStream.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_NULLSTREAM_H 2 | #define CIRCUITOS_NULLSTREAM_H 3 | 4 | 5 | #include "Arduino.h" 6 | 7 | class NullStream : public Stream { 8 | public: 9 | NullStream(); 10 | 11 | int available() override; 12 | int peek() override; 13 | int read() override; 14 | void flush() override; // placeholder to keep CI happy 15 | 16 | size_t write(uint8_t data) override; 17 | size_t write(const uint8_t* buffer, size_t size) override; 18 | 19 | private: 20 | uint8_t bottomlessPit = 0; 21 | }; 22 | 23 | #endif //CIRCUITOS_NULLSTREAM_H 24 | -------------------------------------------------------------------------------- /src/Util/PinMap.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_PINMAP_H 2 | #define CIRCUITOS_PINMAP_H 3 | 4 | #include 5 | 6 | template 7 | struct EnumClassHash { 8 | std::size_t operator()(T t) const { 9 | return static_cast(t); 10 | } 11 | }; 12 | 13 | template 14 | using PinDefMap = std::unordered_map>; 15 | 16 | template 17 | class PinMap { 18 | public: 19 | void set(const PinDefMap& map){ 20 | this->map = map; 21 | } 22 | 23 | int get(T pin){ 24 | const auto i = map.find(pin); 25 | if(i == map.end()) return -1; 26 | return i->second; 27 | } 28 | 29 | private: 30 | PinDefMap map; 31 | 32 | }; 33 | 34 | 35 | #endif //CIRCUITOS_PINMAP_H 36 | -------------------------------------------------------------------------------- /src/Util/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_NVS 4 | #include "SettingsNVS.impl" 5 | #endif 6 | 7 | #ifdef CIRCUITOS_LITTLEFS 8 | #include "SettingsLittleFS.impl" 9 | #endif -------------------------------------------------------------------------------- /src/Util/Settings.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_SETTINGS_H 2 | #define CIRCUITOS_SETTINGS_H 3 | 4 | #include 5 | 6 | #ifdef CIRCUITOS_NVS 7 | #include 8 | 9 | #ifndef nvs_handle_t 10 | typedef nvs_handle nvs_handle_t; 11 | #endif 12 | #endif 13 | 14 | class Settings { 15 | public: 16 | /** 17 | * Initializes the settings 18 | * @param ptr Pointer to the data structure to be used 19 | * @param size Size of the structure 20 | * @return Whether any settings are already saved on the device 21 | */ 22 | static bool init(void* ptr, size_t size); 23 | 24 | static void store(); 25 | static void retrieve(); 26 | 27 | static void* data(); 28 | 29 | private: 30 | static void* _data; 31 | static size_t dataSize; 32 | 33 | #ifdef CIRCUITOS_NVS 34 | static nvs_handle_t handle; 35 | #endif 36 | 37 | }; 38 | 39 | #endif //CIRCUITOS_SETTINGS_H 40 | -------------------------------------------------------------------------------- /src/Util/SettingsLittleFS.impl: -------------------------------------------------------------------------------- 1 | #include "Settings.h" 2 | 3 | #include 4 | #include 5 | 6 | #define Settings_Filename "/CircuitOS" 7 | 8 | void* Settings::_data = nullptr; 9 | size_t Settings::dataSize = 0; 10 | 11 | 12 | bool Settings::init(void* ptr, size_t size){ 13 | _data = ptr; 14 | dataSize = size; 15 | 16 | if(!LittleFS.begin()){ 17 | Serial.println("LittleFS begin error"); 18 | } 19 | 20 | if(!LittleFS.exists(Settings_Filename)){ 21 | return false; 22 | } 23 | 24 | retrieve(); 25 | return true; 26 | } 27 | 28 | void* Settings::data(){ 29 | return _data; 30 | } 31 | 32 | void Settings::store(){ 33 | File file = LittleFS.open(Settings_Filename, "w"); 34 | if(!file){ 35 | Serial.println("Settings store error"); 36 | return; 37 | } 38 | 39 | file.write((byte*) _data, dataSize); 40 | file.close(); 41 | } 42 | 43 | void Settings::retrieve(){ 44 | File file = LittleFS.open(Settings_Filename, "r"); 45 | if(!file){ 46 | Serial.println("Settings load error"); 47 | return; 48 | } 49 | 50 | file.readBytes((char*) _data , dataSize); 51 | file.close(); 52 | } -------------------------------------------------------------------------------- /src/Util/SettingsNVS.impl: -------------------------------------------------------------------------------- 1 | #include "Settings.h" 2 | 3 | void* Settings::_data = nullptr; 4 | size_t Settings::dataSize = 0; 5 | nvs_handle_t Settings::handle = 0; 6 | 7 | // TODO: check if exists 8 | bool Settings::init(void* ptr, size_t size){ 9 | _data = ptr; 10 | dataSize = size; 11 | 12 | esp_err_t err = nvs_open("CircuitOS", NVS_READWRITE, &handle); 13 | if(err != ESP_OK){ 14 | Serial.printf("Non-volatile storage initialization error: %d\n", err); 15 | return false; 16 | } 17 | 18 | retrieve(); 19 | return true; 20 | } 21 | 22 | void* Settings::data(){ 23 | return _data; 24 | } 25 | 26 | void Settings::store(){ 27 | esp_err_t err = nvs_set_blob(handle, "CircuitOS", _data, dataSize); 28 | 29 | if(err != ESP_OK){ 30 | Serial.println("Non-volatile storage store error: %d"); 31 | return; 32 | } 33 | 34 | err = nvs_commit(handle); 35 | if(err != ESP_OK){ 36 | Serial.println("Non-volatile storage commit error: %d"); 37 | } 38 | } 39 | 40 | void Settings::retrieve(){ 41 | esp_err_t err = nvs_get_blob(handle, "CircuitOS", _data, &dataSize); 42 | 43 | if(err != ESP_OK){ 44 | Serial.println("Non-volatile storage retrieval error: %d"); 45 | } 46 | } -------------------------------------------------------------------------------- /src/Util/Task.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Setup.hpp" 2 | 3 | #ifdef CIRCUITOS_TASK 4 | #include "Task.impl" 5 | #endif -------------------------------------------------------------------------------- /src/Util/Task.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_TASK_H 2 | #define CIRCUITOS_TASK_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class Task { 9 | public: 10 | 11 | /** 12 | * 13 | * @param taskName For debugging purposes 14 | * @param fun Task function 15 | */ 16 | Task(std::string taskName, void (*fun)(Task*), size_t stackSize = 2048, void* arg = nullptr); 17 | 18 | /** 19 | * Start the task with optional priority. When a CPU sore becomes available, and more than one task is waiting, the 20 | * one with higher priority will be picked. This can to starvation if there are more tasks waiting for execution. 21 | * 22 | * @see configUSETIMESLICING prevents the scheduler switching between tasks of equal priority when a tick interrupt fires (every 1ms by default - FreeRTOS tick rate) 23 | * 24 | * @param priority 25 | */ 26 | void start(byte priority = 0 /*(1 | portPRIVILEGE_BIT)*/, int8_t core = -1); 27 | 28 | /** 29 | * Stop the task gracefully. The task will stop when the current iteration of the loop finishes. 30 | * @param wait Whether to wait for the task to finish. 31 | */ 32 | void stop(bool wait = false); 33 | 34 | /** 35 | * Stops the task immediately. In other words, kills it. 36 | */ 37 | void kill(); 38 | 39 | /** 40 | * Core pinning. If set to true, each new task will be pinned to a new core. Call again to reset assignment. 41 | * @param pinned 42 | */ 43 | static void setPinned(bool pinned); 44 | 45 | bool running = false; 46 | void* arg = nullptr; 47 | 48 | static void taskFunc(void* arg); 49 | 50 | /** 51 | * Whether the task is stopped. 52 | * @return 53 | */ 54 | bool isStopped() const; 55 | 56 | private: 57 | std::string taskName; 58 | void (*func)(Task*) = nullptr; 59 | size_t stackSize = 2048; 60 | 61 | TaskHandle_t tHandle; 62 | 63 | static bool pinnedTasks; 64 | static uint8_t usedCores; 65 | 66 | bool stopped = true; 67 | }; 68 | 69 | 70 | #endif //CIRCUITOS_TASK_H 71 | -------------------------------------------------------------------------------- /src/Util/Task.impl: -------------------------------------------------------------------------------- 1 | #include "Task.h" 2 | 3 | #include 4 | 5 | bool Task::pinnedTasks = false; 6 | uint8_t Task::usedCores = 0; 7 | 8 | Task::Task(std::string taskName, void (* fun)(Task*), size_t stackSize, void* arg) : taskName(std::move(taskName)), func(fun), stackSize(stackSize), arg(arg){ 9 | 10 | } 11 | 12 | void Task::taskFunc(void* arg){ 13 | Task* task = static_cast(arg); 14 | task->func(task); 15 | 16 | task->running = false; // in case the Task exited by itself 17 | task->stopped = true; 18 | 19 | if(pinnedTasks){ 20 | usedCores--; 21 | } 22 | 23 | vTaskDelete(NULL); 24 | } 25 | 26 | void Task::start(byte priority, int8_t core){ 27 | running = true; 28 | stopped = false; 29 | 30 | if(core != -1){ 31 | xTaskCreatePinnedToCore(Task::taskFunc, taskName.c_str(), stackSize, this, priority, &tHandle, core); 32 | }else if(pinnedTasks){ 33 | Serial.printf("Creating task on proc %d\n", usedCores); 34 | xTaskCreatePinnedToCore(Task::taskFunc, taskName.c_str(), stackSize, this, priority, &tHandle, usedCores++); 35 | }else{ 36 | /** task function, task name, stack size, parameter, priority, handle */ 37 | if(xTaskCreate(Task::taskFunc, taskName.c_str(), stackSize, this, priority, &tHandle) != pdPASS){ 38 | Serial.printf("Task %s start failed\n", taskName.c_str()); 39 | } 40 | } 41 | } 42 | 43 | void Task::stop(bool wait){ 44 | running = false; 45 | 46 | if(wait){ 47 | while(!stopped){ 48 | delay(1); 49 | } 50 | } 51 | } 52 | 53 | void Task::kill(){ 54 | if(pinnedTasks){ 55 | usedCores--; 56 | } 57 | 58 | vTaskDelete(tHandle); 59 | 60 | running = false; 61 | stopped = true; 62 | } 63 | 64 | void Task::setPinned(bool pinned){ 65 | Task::pinnedTasks = pinned; 66 | usedCores = 0; 67 | } 68 | 69 | bool Task::isStopped() const{ 70 | return stopped; 71 | } 72 | -------------------------------------------------------------------------------- /src/Util/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | struct hw_timer_reg_s { 4 | union { 5 | struct { 6 | uint32_t reserved0: 10; 7 | uint32_t alarm_en: 1; /*When set alarm is enabled*/ 8 | uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/ 9 | uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/ 10 | uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/ 11 | uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ 12 | uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/ 13 | uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/ 14 | }; 15 | uint32_t val; 16 | } config; 17 | uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/ 18 | uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/ 19 | uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 current value will be stored in registers above)*/ 20 | uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/ 21 | uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the alarm*/ 22 | uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ 23 | uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/ 24 | uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/ 25 | }; 26 | 27 | struct hw_timer_s { 28 | hw_timer_reg_t* dev; 29 | uint8_t num; 30 | uint8_t group; 31 | uint8_t timer; 32 | portMUX_TYPE lock; 33 | }; 34 | 35 | void IRAM_ATTR CM::timerStop(hw_timer_t* timer){ 36 | timer->dev->config.enable = 0; 37 | } 38 | 39 | void IRAM_ATTR CM::timerWrite(hw_timer_t* timer, uint64_t val){ 40 | timer->dev->load_high = (uint32_t)(val >> 32); 41 | timer->dev->load_low = (uint32_t)(val); 42 | timer->dev->reload = 1; 43 | } 44 | 45 | void IRAM_ATTR CM::timerAlarmWrite(hw_timer_t* timer, uint64_t alarm_value, bool autoreload){ 46 | timer->dev->alarm_high = (uint32_t)(alarm_value >> 32); 47 | timer->dev->alarm_low = (uint32_t)alarm_value; 48 | timer->dev->config.autoreload = autoreload; 49 | } 50 | 51 | void IRAM_ATTR CM::timerAlarmDisable(hw_timer_t* timer){ 52 | timer->dev->config.alarm_en = 0; 53 | } 54 | 55 | uint64_t IRAM_ATTR CM::timerRead(hw_timer_t* timer){ 56 | timer->dev->update = 1; 57 | while(timer->dev->update) delayMicroseconds(1); 58 | uint64_t h = timer->dev->cnt_high; 59 | uint64_t l = timer->dev->cnt_low; 60 | return (h << 32) | l; 61 | } 62 | -------------------------------------------------------------------------------- /src/Util/Timer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_TIMER_H 2 | #define CIRCUITOS_TIMER_H 3 | 4 | #include 5 | 6 | #define dis(timer) do { CM::timerAlarmDisable(timer); CM::timerStop(timer); CM::timerWrite(timer, 0); } while(0) 7 | 8 | struct hw_timer_reg_s; 9 | typedef struct hw_timer_reg_s hw_timer_reg_t; 10 | 11 | struct hw_timer_s; 12 | typedef struct hw_timer_s hw_timer_t; 13 | 14 | typedef struct hw_timer_s hw_timer_t; 15 | 16 | namespace CM { 17 | void IRAM_ATTR timerStop(hw_timer_t* timer); 18 | 19 | void IRAM_ATTR timerWrite(hw_timer_t* timer, uint64_t val); 20 | 21 | void IRAM_ATTR timerAlarmWrite(hw_timer_t* timer, uint64_t alarm_value, bool autoreload); 22 | 23 | void IRAM_ATTR timerAlarmDisable(hw_timer_t* timer); 24 | 25 | uint64_t IRAM_ATTR timerRead(hw_timer_t* timer); 26 | } 27 | 28 | #endif //CIRCUITOS_TIMER_H 29 | -------------------------------------------------------------------------------- /src/Util/Vector.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_VECTOR_H 2 | #define CIRCUITOS_VECTOR_H 3 | 4 | #include 5 | #include 6 | 7 | template 8 | class Vector : public std::vector { 9 | public: 10 | using std::vector::vector; 11 | 12 | /** 13 | * Change the position of of an element. All elements after the new position, including the element that was 14 | * formerly at that position, will be moved one position to the right. 15 | * @param oldPos Index of the element to be moved. 16 | * @param newPos Index of the position where the element should be moved. 17 | */ 18 | void relocate(uint oldPos, uint newPos); 19 | 20 | /** 21 | * Swap the position of two elements. 22 | * @param posA First element. 23 | * @param posB Second element. 24 | */ 25 | void swap(uint posA, uint posB); 26 | 27 | /** 28 | * Remove an element from the array. 29 | * @param element Index of the element to be removed. 30 | */ 31 | void remove(uint i); 32 | 33 | /** 34 | * Find the index of an element. 35 | * @param element The element. 36 | * @return Index of the element. -1 (uint max) if element isn't in the array. 37 | */ 38 | uint indexOf(T element); 39 | 40 | }; 41 | 42 | template 43 | void Vector::relocate(uint oldPos, uint newPos){ 44 | if(oldPos == newPos) return; 45 | 46 | T tmp; 47 | memmove(&tmp, &this->data()[oldPos], sizeof(T)); 48 | 49 | if(newPos < oldPos){ 50 | memmove(&this->data()[newPos+1], &this->data()[newPos], (oldPos - newPos) * sizeof(T)); 51 | }else{ 52 | memmove(&this->data()[oldPos], &this->data()[oldPos+1], (newPos - oldPos) * sizeof(T)); 53 | } 54 | 55 | memmove(&this->data()[newPos], &tmp, sizeof(T)); 56 | } 57 | 58 | template 59 | void Vector::swap(uint posA, uint posB){ 60 | T tmp; 61 | memmove(&tmp, &this->data()[posA], sizeof(T)); 62 | memmove(&this->data()[posA], &this->data()[posB], sizeof(T)); 63 | memmove(&this->data()[posB], &tmp, sizeof(T)); 64 | } 65 | 66 | template 67 | void Vector::remove(uint i){ 68 | this->erase(this->begin() + i); 69 | } 70 | 71 | template 72 | uint Vector::indexOf(T element){ 73 | for(int i = 0; i < this->size(); i++){ 74 | if(this->data()[i] == element) return i; 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | #endif //CIRCUITOS_VECTOR_H 81 | -------------------------------------------------------------------------------- /src/Util/WithListeners.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_WITHLISTENERS_H 2 | #define CIRCUITOS_WITHLISTENERS_H 3 | 4 | #include 5 | #include 6 | 7 | template 8 | class WithListeners { 9 | public: 10 | void addListener(T* listener) { 11 | listeners.insert(listener); 12 | } 13 | 14 | void removeListener(T* listener){ 15 | auto it = listeners.find(listener); 16 | if(it == listeners.end()) return; 17 | 18 | listeners.erase(it); 19 | } 20 | 21 | protected: 22 | /** 23 | * Notify all listeners that something has changed. 24 | * 25 | * @param func Function to be called on all listeners 26 | */ 27 | void iterateListeners(std::function func){ 28 | auto listenersCopy(listeners); 29 | for(auto listener : listenersCopy){ 30 | func(listener); 31 | } 32 | } 33 | 34 | void reserve(size_t count){ 35 | listeners.reserve(count); 36 | } 37 | 38 | private: 39 | std::unordered_set listeners; 40 | }; 41 | #endif //CIRCUITOS_WITHLISTENERS_H 42 | -------------------------------------------------------------------------------- /src/Util/gifdec.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCUITOS_GIFDEC_H 2 | #define CIRCUITOS_GIFDEC_H 3 | /* 4 | Modified version of the excellent and lightweight gifdec library by Marcel Rodrigues. 5 | (https://github.com/lecram/gifdec) 6 | Library is modified to work with the filesystem File class from ESP's core. 7 | */ 8 | #include 9 | #include 10 | 11 | namespace CircuitOS { 12 | 13 | typedef struct gd_Palette { 14 | int size; 15 | uint8_t colors[0x100 * 3]; 16 | } gd_Palette; 17 | 18 | typedef struct gd_GCE { 19 | uint16_t delay; 20 | uint8_t tindex; 21 | uint8_t disposal; 22 | int input; 23 | int transparency; 24 | } gd_GCE; 25 | 26 | typedef struct gd_GIF { 27 | fs::File fd; 28 | off_t anim_start; 29 | uint16_t width, height; 30 | uint16_t depth; 31 | uint16_t loop_count; 32 | gd_GCE gce; 33 | gd_Palette *palette; 34 | gd_Palette lct, gct; 35 | void (*plain_text)( 36 | struct gd_GIF *gif, uint16_t tx, uint16_t ty, 37 | uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch, 38 | uint8_t fg, uint8_t bg 39 | ); 40 | void (*comment)(struct gd_GIF *gif); 41 | void (*application)(struct gd_GIF *gif, char id[8], char auth[3]); 42 | uint16_t fx, fy, fw, fh; 43 | uint8_t bgindex; 44 | uint8_t *canvas, *frame; 45 | } gd_GIF; 46 | 47 | gd_GIF *gd_open_gif(fs::File file); 48 | int gd_get_frame(gd_GIF *gif); 49 | void gd_render_frame(gd_GIF *gif, uint8_t *buffer, bool monochrome = 1); 50 | int gd_is_bgcolor(gd_GIF *gif, uint8_t color[3]); 51 | void gd_rewind(gd_GIF *gif); 52 | void gd_close_gif(gd_GIF *gif); 53 | 54 | }; 55 | 56 | #endif /* CIRCUITOS_GIFDEC_H */ 57 | --------------------------------------------------------------------------------