├── .gitignore ├── img ├── cpm.png ├── mysid.jpg ├── apmode.png ├── thesid.jpg ├── cp_setup.png ├── cps-frag.png ├── irremote.jpg ├── stamode-car.png ├── stamode-car2.png ├── stamode-home.png ├── stamode-mqtt.png ├── stamode-bttfn.png ├── family-wifi-bttfn.png └── family-wifi-mqtt.png ├── CheatSheet.pdf ├── google75052746a9363b19.html ├── install ├── sid-A10001986.ino.nodemcu-32s.bin └── README.md ├── src ├── src │ ├── WiFiManager │ │ ├── wm_local.h │ │ ├── LICENSE │ │ ├── wm_strings_en.h │ │ └── WiFiManager.h │ └── arduinoFFT │ │ ├── types.h │ │ ├── defs.h │ │ ├── arduinoFFT.h │ │ └── arduinoFFT.cpp ├── sid_global.h ├── sid_sa.h ├── sid_snake.h ├── sid_wifi.h ├── sid_siddly.h ├── sid_main.h ├── siddisplay.h ├── input.h ├── sid_settings.h ├── mqtt.h ├── sid_snake.cpp ├── input.cpp ├── sid_sa.cpp ├── sid_siddly.cpp ├── siddisplay.cpp └── sid_font.h ├── platformio.ini └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /.pio 2 | /.vscode 3 | SID.code-workspace 4 | -------------------------------------------------------------------------------- /img/cpm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/cpm.png -------------------------------------------------------------------------------- /img/mysid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/mysid.jpg -------------------------------------------------------------------------------- /CheatSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/CheatSheet.pdf -------------------------------------------------------------------------------- /google75052746a9363b19.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google75052746a9363b19.html -------------------------------------------------------------------------------- /img/apmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/apmode.png -------------------------------------------------------------------------------- /img/thesid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/thesid.jpg -------------------------------------------------------------------------------- /img/cp_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/cp_setup.png -------------------------------------------------------------------------------- /img/cps-frag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/cps-frag.png -------------------------------------------------------------------------------- /img/irremote.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/irremote.jpg -------------------------------------------------------------------------------- /img/stamode-car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/stamode-car.png -------------------------------------------------------------------------------- /img/stamode-car2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/stamode-car2.png -------------------------------------------------------------------------------- /img/stamode-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/stamode-home.png -------------------------------------------------------------------------------- /img/stamode-mqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/stamode-mqtt.png -------------------------------------------------------------------------------- /img/stamode-bttfn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/stamode-bttfn.png -------------------------------------------------------------------------------- /img/family-wifi-bttfn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/family-wifi-bttfn.png -------------------------------------------------------------------------------- /img/family-wifi-mqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/img/family-wifi-mqtt.png -------------------------------------------------------------------------------- /install/sid-A10001986.ino.nodemcu-32s.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CircuitSetup/SID/main/install/sid-A10001986.ino.nodemcu-32s.bin -------------------------------------------------------------------------------- /src/src/WiFiManager/wm_local.h: -------------------------------------------------------------------------------- 1 | /** 2 | * WiFiManager.h 3 | * 4 | * Based on: 5 | * WiFiManager, a library for the ESP32/Arduino platform 6 | * Creator tzapu (tablatronix) 7 | * Version 2.0.15 8 | * License MIT 9 | * 10 | * Adapted by Thomas Winischhofer (A10001986) 11 | */ 12 | 13 | #ifndef wm_local_h 14 | #define wm_local_h 15 | 16 | //#define _A10001986_DBG 17 | //#define _A10001986_V_DBG 18 | 19 | #define WM_MDNS 20 | #define WM_PARAM2 21 | // #define WM_AP_STATIC_IP 22 | // #define WM_APCALLBACK 23 | // #define WM_PRECONNECTCB 24 | // #define WM_PRESAVECB 25 | // #define WM_EVENTCB 26 | // #define WM_ADDLGETTERS 27 | // #define WM_ADDLSETTERS 28 | 29 | #ifdef WM_PARAM2 30 | #define WM_PARAM2_CAPTION "HA/MQTT Settings" 31 | #define WM_PARAM2_TITLE "HA/MQTT Settings" 32 | #endif 33 | 34 | #endif -------------------------------------------------------------------------------- /install/README.md: -------------------------------------------------------------------------------- 1 | This folder holds a binary of the current SID firmware, ready for upload to your device. 2 | 3 | ## Firmware Installation 4 | 5 | If a previous version of the SID firmware is installed on your device, you can update easily using the pre-compiled binary. Enter the Config Portal, click on "Update" and select the pre-compiled binary file provided in this repository ([install/sid-A10001986.ino.nodemcu-32s.bin](https://github.com/realA10001986/SID/blob/main/install/sid-A10001986.ino.nodemcu-32s.bin)). 6 | 7 | If you are using a fresh ESP32 board, please see [sid-A10001986.ino](https://github.com/realA10001986/SID/blob/main/sid-A10001986/sid-A10001986.ino) for detailed build and upload information, or, if you don't want to deal with source code, compilers and all that nerd stuff, go [here](https://install.out-a-ti.me) and follow the instructions. 8 | 9 | *Important: After a firmware update, a "wait" symbol (hourglass) might be shown for a short while after reboot. Do NOT unplug the device during this time.* 10 | -------------------------------------------------------------------------------- /src/src/WiFiManager/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tzapu 4 | Adaptions: Copyright (c) Thomas Winischhofer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = esp32dev 13 | 14 | [env:esp32dev] 15 | platform = platformio/espressif32 16 | framework = arduino 17 | board = nodemcu-32s 18 | platform_packages = platformio/framework-arduinoespressif32 19 | board_build.f_cpu = 240000000L 20 | board_build.flash_mode = qio 21 | lib_deps = 22 | ;wnatth3/WiFiManager @ 2.0.16-rc.2 ;https://github.com/tzapu/WiFiManager.git 23 | ArduinoJson @ ^6.21.5 24 | upload_speed = 921600 25 | monitor_speed = 115200 26 | build_flags = 27 | -std=gnu++11 28 | -mtarget-align 29 | ;see sid_global.h for full explanations of these options 30 | #-DSID_DBG ;enables serial debug 31 | #-DUSE_SPIFFS ;use SPIFFS for arduinoespressif32 < 2.0, otherwise use LittleFS - If LittleFS uncomment board_build.filesystem below 32 | 33 | board_build.filesystem = LittleFS ;uncomment if using LittleFS - make sure USE_SPIFFS IS commented above 34 | build_src_flags = 35 | -DDEBUG_PORT=Serial 36 | -ggdb 37 | ;uncomment the following to use the esp32 exception decoder 38 | #monitor_filters = esp32_exception_decoder 39 | #build_type = debug -------------------------------------------------------------------------------- /src/src/arduinoFFT/types.h: -------------------------------------------------------------------------------- 1 | //useful things to include in code 2 | 3 | #ifndef TYPES_H 4 | #define TYPES_H 5 | 6 | #ifndef WIN32 7 | // true/false defines 8 | #define FALSE 0 9 | #define TRUE -1 10 | #endif 11 | 12 | // datatype definitions macros 13 | typedef unsigned char u08; 14 | typedef signed char s08; 15 | typedef unsigned short u16; 16 | typedef signed short s16; 17 | typedef unsigned long u32; 18 | typedef signed long s32; 19 | typedef unsigned long long u64; 20 | typedef signed long long s64; 21 | 22 | // #ifndef __AVR__ 23 | #ifdef __MBED__ 24 | // use inttypes.h instead 25 | // C99 standard integer type definitions 26 | typedef unsigned char uint8_t; 27 | typedef signed char int8_t; 28 | typedef unsigned short uint16_t; 29 | typedef signed short int16_t; 30 | /*typedef unsigned long uint32_t; 31 | typedef signed long int32_t; 32 | typedef unsigned long uint64_t; 33 | typedef signed long int64_t; 34 | */ 35 | #endif 36 | 37 | // maximum value that can be held 38 | // by unsigned data types (8,16,32bits) 39 | #define MAX_U08 255 40 | #define MAX_U16 65535 41 | #define MAX_U32 4294967295 42 | 43 | // maximum values that can be held 44 | // by signed data types (8,16,32bits) 45 | #define MIN_S08 -128 46 | #define MAX_S08 127 47 | #define MIN_S16 -32768 48 | #define MAX_S16 32767 49 | #define MIN_S32 -2147483648 50 | #define MAX_S32 2147483647 51 | 52 | #ifndef WIN32 53 | // more type redefinitions 54 | typedef unsigned char BOOL; 55 | typedef unsigned char BYTE; 56 | typedef unsigned int WORD; 57 | typedef unsigned long DWORD; 58 | 59 | typedef unsigned char UCHAR; 60 | typedef unsigned int UINT; 61 | typedef unsigned short USHORT; 62 | typedef unsigned long ULONG; 63 | 64 | typedef char CHAR; 65 | typedef int INT; 66 | typedef long LONG; 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT NON-AI License 2 | 3 | Copyright (c) 2023-2024 Thomas Winischhofer 4 | 5 | Home Assistant MQTT protocol code: 6 | Copyright (c) 2008-2020 Nicholas O'Leary 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | In addition, the following restrictions apply: 19 | 20 | 1. The Software and any modifications made to it may not be used for the 21 | purpose of training or improving machine learning algorithms, including but 22 | not limited to artificial intelligence, natural language processing, or data 23 | mining. This condition applies to any derivatives, modifications, or updates 24 | based on the Software code. Any usage of the Software in an AI-training dataset 25 | is considered a breach of this License. 26 | 27 | 2. The Software may not be included in any dataset used for training or 28 | improving machine learning algorithms, including but not limited to artificial 29 | intelligence, natural language processing, or data mining. 30 | 31 | 3. Any person or organization found to be in violation of these restrictions 32 | will be subject to legal action and may be held liable for any damages resulting 33 | from such use. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 40 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 41 | SOFTWARE. 42 | -------------------------------------------------------------------------------- /src/sid_global.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Global definitions 9 | */ 10 | 11 | #ifndef _SID_GLOBAL_H 12 | #define _SID_GLOBAL_H 13 | 14 | /************************************************************************* 15 | *** Version Strings *** 16 | *************************************************************************/ 17 | 18 | #define SID_VERSION "V1.61" 19 | #define SID_VERSION_EXTRA "NOV262025" 20 | 21 | //#define SID_DBG // debug output on Serial 22 | 23 | /************************************************************************* 24 | *** Miscellaneous *** 25 | *************************************************************************/ 26 | 27 | // Uncomment for HomeAssistant MQTT protocol support 28 | #define SID_HAVEMQTT 29 | 30 | // External time travel lead time, as defined by TCD firmware 31 | // If SID is connected to TCD by wire, and the option "Signal Time Travel 32 | // without 5s lead" is set on the TCD, the SID option "TCD signals without 33 | // lead" must be set, too. 34 | #define ETTO_LEAD 5000 35 | 36 | // Use SPIFFS (if defined) or LittleFS (if undefined; esp32-arduino >= 2.x) 37 | //#define USE_SPIFFS 38 | 39 | /************************************************************************* 40 | *** esp32-arduino version detection *** 41 | *************************************************************************/ 42 | 43 | #if defined __has_include && __has_include() 44 | #include 45 | #ifdef ESP_ARDUINO_VERSION_MAJOR 46 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2,0,8) 47 | #define HAVE_GETNEXTFILENAME 48 | #endif 49 | #endif 50 | #endif 51 | 52 | /************************************************************************* 53 | *** GPIO pins *** 54 | *************************************************************************/ 55 | 56 | // IR Remote 57 | #define IRREMOTE_PIN 27 58 | 59 | // IR feedback 60 | #define IR_FB_PIN 17 61 | 62 | // Time Travel button (or TCD input trigger) 63 | #define TT_IN_PIN 13 64 | 65 | // I2S audio pins 66 | #define I2S_BCLK_PIN 26 67 | #define I2S_LRCLK_PIN 25 68 | #define I2S_DIN_PIN 33 69 | 70 | // SD Card pins 71 | #define SD_CS_PIN 5 72 | #define SPI_MOSI_PIN 23 73 | #define SPI_MISO_PIN 19 74 | #define SPI_SCK_PIN 18 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/src/arduinoFFT/defs.h: -------------------------------------------------------------------------------- 1 | /*! \file avrlibdefs.h \brief AVRlib global defines and macros. */ 2 | //***************************************************************************** 3 | // 4 | // File Name : 'avrlibdefs.h' 5 | // Title : AVRlib global defines and macros include file 6 | // Author : Pascal Stang 7 | // Created : 7/12/2001 8 | // Revised : 9/30/2002 9 | // Version : 1.1 10 | // Target MCU : Atmel AVR series 11 | // Editor Tabs : 4 12 | // 13 | // Description : This include file is designed to contain items useful to all 14 | // code files and projects, regardless of specific implementation. 15 | // 16 | // This code is distributed under the GNU Public License 17 | // which can be found at http://www.gnu.org/licenses/gpl.txt 18 | // 19 | //***************************************************************************** 20 | 21 | 22 | #ifndef AVRLIBDEFS_H 23 | #define AVRLIBDEFS_H 24 | 25 | //#define F_CPU 4000000 26 | #define MEM_TYPE 1 27 | 28 | // Code compatibility to new AVR-libc 29 | // outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli() 30 | #ifndef outb 31 | #define outb(addr, data) addr = (data) 32 | #endif 33 | #ifndef inb 34 | #define inb(addr) (addr) 35 | #endif 36 | #ifndef outw 37 | #define outw(addr, data) addr = (data) 38 | #endif 39 | #ifndef inw 40 | #define inw(addr) (addr) 41 | #endif 42 | #ifndef BV 43 | #define BV(bit) (1<<(bit)) 44 | #endif 45 | //#ifndef cbi 46 | // #define cbi(reg,bit) reg &= ~(BV(bit)) 47 | //#endif 48 | //#ifndef sbi 49 | // #define sbi(reg,bit) reg |= (BV(bit)) 50 | //#endif 51 | #ifndef cli 52 | #define cli() __asm__ __volatile__ ("cli" ::) 53 | #endif 54 | #ifndef sei 55 | #define sei() __asm__ __volatile__ ("sei" ::) 56 | #endif 57 | 58 | // support for individual port pin naming in the mega128 59 | // see port128.h for details 60 | #ifdef __AVR_ATmega128__ 61 | // not currently necessary due to inclusion 62 | // of these defines in newest AVR-GCC 63 | // do a quick test to see if include is needed 64 | #ifndef PD0 65 | //#include "port128.h" 66 | #endif 67 | #endif 68 | 69 | // use this for packed structures 70 | // (this is seldom necessary on an 8-bit architecture like AVR, 71 | // but can assist in code portability to AVR) 72 | #define GNUC_PACKED __attribute__((packed)) 73 | 74 | // port address helpers 75 | #define DDR(x) ((x)-1) // address of data direction register of port x 76 | #define PIN(x) ((x)-2) // address of input register of port x 77 | 78 | // MIN/MAX/ABS macros 79 | #define MIN(a,b) ((ab)?(a):(b)) 81 | #define ABS(x) ((x>0)?(x):(-x)) 82 | 83 | // constants 84 | #define PI 3.14159265359 85 | 86 | //Math 87 | #define sq(x) ((x)*(x)) 88 | #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/sid_sa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Spectrum Analyzer 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | #ifndef _SID_SA_H 52 | #define _SID_SA_H 53 | 54 | extern bool saActive; // Read only! 55 | extern bool doPeaks; 56 | 57 | #define SA_START_DELAY 1000 // Delay to skip the mic's startup noise 58 | 59 | void sa_activate(bool init = true, unsigned long start_Delay = SA_START_DELAY); 60 | void sa_deactivate(); 61 | 62 | int sa_setAmpFact(int newAmpFact); 63 | 64 | void sa_loop(); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/sid_snake.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Snake 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SID_SN_H 53 | #define _SID_SN_H 54 | 55 | extern bool snActive; // read only!!! 56 | 57 | void sn_init(); // start game 58 | void sn_loop(); // game loop 59 | void sn_end(); // end game (quit) 60 | void sn_newGame(); // restart game (when active) 61 | void sn_pause(); // pause game (toggle) 62 | void sn_moveRight(); // user input: move right 63 | void sn_moveLeft(); // user input: move left 64 | void sn_moveDown(); // user input: move down 65 | void sn_moveUp(); // user input: move up 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/sid_wifi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * WiFi and Config Portal handling 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SID_WIFI_H 53 | #define _SID_WIFI_H 54 | 55 | extern bool wifiSetupDone; 56 | extern bool wifiIsOff; 57 | extern bool wifiAPIsOff; 58 | extern bool wifiInAPMode; 59 | 60 | void wifi_setup(); 61 | void wifi_setup2(); 62 | void wifi_loop(); 63 | void wifiOn(unsigned long newDelay = 0, bool alsoInAPMode = false, bool deferConfigPortal = false); 64 | bool wifiOnWillBlock(); 65 | void wifiStartCP(); 66 | 67 | void updateConfigPortalValues(); 68 | void updateConfigPortalStrictValue(); 69 | 70 | bool wifi_getIP(uint8_t& a, uint8_t& b, uint8_t& c, uint8_t& d); 71 | bool isIp(char *str); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/sid_siddly.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Siddly 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SID_SI_H 53 | #define _SID_SI_H 54 | 55 | extern bool siActive; // read only!!! 56 | 57 | void si_init(); // start game 58 | void si_loop(); // game loop 59 | void si_end(); // end game (quit) 60 | void si_newGame(); // restart game (when active) 61 | void si_pause(); // pause game (toggle) 62 | void si_moveRight(); // user input: move right 63 | void si_moveLeft(); // user input: move left 64 | void si_rotate(); // user input: rotate (left) 65 | void si_moveDown(); // user input: move down 66 | void si_fallDown(); // user input: fall down 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/sid_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Main controller 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | #ifndef _SID_MAIN_H 52 | #define _SID_MAIN_H 53 | 54 | #include "siddisplay.h" 55 | 56 | extern unsigned long powerupMillis; 57 | 58 | extern sidDisplay sid; 59 | 60 | #define SID_MAX_IDLE_MODE 5 61 | extern uint16_t idleMode; 62 | extern bool strictMode; 63 | 64 | // Number of IR keys 65 | #define NUM_IR_KEYS 17 66 | 67 | extern bool irLocked; 68 | 69 | extern bool TCDconnected; 70 | 71 | extern bool FPBUnitIsOn; 72 | extern bool sidNM; 73 | extern bool blockScan; 74 | 75 | extern bool TTrunning; 76 | extern bool IRLearning; 77 | 78 | extern bool networkTimeTravel; 79 | extern bool networkTCDTT; 80 | extern bool networkReentry; 81 | extern bool networkAbort; 82 | extern bool networkAlarm; 83 | extern uint16_t networkLead; 84 | extern uint16_t networkP1; 85 | 86 | extern uint32_t myRemID; 87 | 88 | extern bool doPrepareTT; 89 | extern bool doWakeup; 90 | 91 | extern bool sidBusy; 92 | 93 | void main_boot(); 94 | void main_setup(); 95 | void main_loop(); 96 | 97 | void flushDelayedSave(); 98 | 99 | void showWaitSequence(bool force = false); 100 | void endWaitSequence(); 101 | 102 | void allOff(); 103 | void prepareReboot(); 104 | 105 | void populateIRarray(uint32_t *irkeys, int index); 106 | void copyIRarray(uint32_t *irkeys, int index); 107 | 108 | void showWordSequence(const char *text, int speed = 3); 109 | 110 | void mydelay(unsigned long mydel, bool withIR); 111 | 112 | void prepareTT(); 113 | void wakeup(); 114 | 115 | void setIdleMode(int idleNo); 116 | 117 | void switch_to_idle(); 118 | void switch_to_sa(); 119 | 120 | void addCmdQueue(uint32_t command); 121 | void bttfn_loop(); 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /src/siddisplay.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * SIDDisplay Class: Handles the SID LEDs 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SIDDISPLAY_H 53 | #define _SIDDISPLAY_H 54 | 55 | // Special sequences 56 | #define SID_SS_REMSTART 1 57 | #define SID_SS_REMEND 2 58 | #define SID_SS_IRBADINP 3 59 | #define SID_SS_MAX SID_SS_IRBADINP 60 | 61 | #define SD_BUF_SIZE 16 // Buffer size in words (16bit) 62 | 63 | class sidDisplay { 64 | 65 | public: 66 | 67 | sidDisplay(uint8_t address1, uint8_t address2); 68 | void begin(); 69 | void on(); 70 | void off(); 71 | 72 | void lampTest(); 73 | 74 | void clearBuf(); 75 | 76 | uint8_t setBrightness(uint8_t level, bool setInitial = false); 77 | void resetBrightness(); 78 | uint8_t setBrightnessDirect(uint8_t level); 79 | uint8_t getBrightness(); 80 | 81 | void show(); 82 | 83 | void clearDisplayDirect(); 84 | 85 | void drawBar(uint8_t bar, uint8_t bottom, uint8_t top); 86 | void drawBarWithHeight(uint8_t bar, uint8_t height); 87 | void clearBar(uint8_t bar); 88 | void drawDot(uint8_t bar, uint8_t dot_y); 89 | 90 | void drawFieldAndShow(uint8_t *fieldData); 91 | 92 | void drawLetterAndShow(char alpha, int x = 0, int y = 8); 93 | void drawLetterMask(char alpha, int x, int y); 94 | void drawClockAndShow(uint8_t *dateBuf, int dx, int dy); 95 | 96 | void specialSig(uint8_t sig); 97 | bool specialTrigger(); 98 | 99 | private: 100 | void superImposeSpecSig(); 101 | void directCmd(uint8_t val); 102 | 103 | uint8_t _address[2] = { 0, 0 }; 104 | 105 | uint8_t _brightness = 15; // current display brightness 106 | uint8_t _origBrightness = 15; // value from settings 107 | 108 | uint8_t _specialSig = 0; 109 | unsigned long _specialSigNow = 0; 110 | bool _specialTrigger = false; 111 | 112 | uint16_t _displayBuffer[SD_BUF_SIZE]; 113 | 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * FCRemote Class: Remote control handling 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SIDINPUT_H 53 | #define _SIDINPUT_H 54 | 55 | /* 56 | * IRRemote class 57 | */ 58 | 59 | #define IRBUFSIZE 100 60 | 61 | typedef enum { 62 | IRSTATE_IDLE, 63 | IRSTATE_LIGHT, 64 | IRSTATE_DARK, 65 | IRSTATE_STOP 66 | } IRState; 67 | 68 | class IRRemote { 69 | 70 | public: 71 | IRRemote(uint8_t timerno, uint8_t ir_pin); 72 | void begin(); 73 | 74 | bool loop(); 75 | uint32_t readHash(); 76 | void resume(); 77 | 78 | private: 79 | uint32_t compare(unsigned int oldval, unsigned int newval); 80 | bool calcHash(); 81 | 82 | uint8_t _timer_no = 0; 83 | hw_timer_t *_IRTimer = NULL; 84 | 85 | uint32_t _buflen; 86 | uint32_t _buf[IRBUFSIZE]; 87 | uint32_t _hvalue; 88 | 89 | unsigned long _prevTime; 90 | uint32_t _prevHash; 91 | }; 92 | 93 | 94 | /* 95 | * SIDButton class 96 | */ 97 | 98 | typedef enum { 99 | TCBS_IDLE, 100 | TCBS_PRESSED, 101 | TCBS_RELEASED, 102 | TCBS_LONGPRESS, 103 | TCBS_LONGPRESSEND 104 | } ButtonState; 105 | 106 | class SIDButton { 107 | 108 | public: 109 | SIDButton(const int pin, const bool activeLow = true, const bool pullupActive = true, const bool pulldownActive = false); 110 | 111 | void setTiming(const int dticks, const int pticks, const int lticks); 112 | 113 | void attachPress(void (*newFunction)(void)); 114 | void attachLongPressStart(void (*newFunction)(void)); 115 | void attachLongPressStop(void (*newFunction)(void)); 116 | 117 | void scan(void); 118 | 119 | private: 120 | 121 | void reset(void); 122 | void transitionTo(ButtonState nextState); 123 | 124 | void (*_pressFunc)(void) = NULL; 125 | void (*_longPressStartFunc)(void) = NULL; 126 | void (*_longPressStopFunc)(void) = NULL; 127 | 128 | int _pin; 129 | 130 | unsigned int _debounceDur = 50; 131 | unsigned int _pressDur = 400; 132 | unsigned int _longPressDur = 800; 133 | 134 | int _buttonPressed; 135 | 136 | ButtonState _state = TCBS_IDLE; 137 | ButtonState _lastState = TCBS_IDLE; 138 | 139 | unsigned long _startTime; 140 | 141 | bool _pressNotified = false; 142 | }; 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/src/arduinoFFT/arduinoFFT.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | FFT library 4 | Copyright (C) 2010 Didier Longueville 5 | Copyright (C) 2014 Enrique Condes 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | #ifndef ArduinoFFT_h /* Prevent loading library twice */ 23 | #define ArduinoFFT_h 24 | 25 | //#define FFT_DOUBLE 26 | 27 | #ifdef FFT_DOUBLE 28 | #define FTYPE double 29 | #define FFT_COS cos 30 | #define FFT_SQRT sqrt 31 | #else 32 | #define FTYPE float 33 | #define FFT_COS cosf 34 | #define FFT_SQRT sqrtf 35 | #endif 36 | 37 | #ifdef ARDUINO 38 | #if ARDUINO >= 100 39 | #include "Arduino.h" 40 | #else 41 | #include "WProgram.h" /* This is where the standard Arduino code lies */ 42 | #endif 43 | #else 44 | #include 45 | #include 46 | 47 | #ifdef __AVR__ 48 | #include 49 | #include 50 | #endif 51 | #include "defs.h" 52 | #include "types.h" 53 | #include 54 | 55 | #endif 56 | 57 | // Define this to use a low-precision square root approximation instead of the 58 | // regular sqrt() call 59 | // This might only work for specific use cases, but is significantly faster. 60 | // Only works for ArduinoFFT. 61 | // #define FFT_SQRT_APPROXIMATION 62 | 63 | #ifdef FFT_SQRT_APPROXIMATION 64 | #include 65 | #else 66 | #define sqrt_internal sqrt 67 | #endif 68 | 69 | enum class FFTDirection { Reverse, Forward }; 70 | 71 | enum class FFTWindow { 72 | Rectangle, // rectangle (Box car) 73 | Hamming, // hamming 74 | Hann, // hann 75 | Triangle, // triangle (Bartlett) 76 | Nuttall, // nuttall 77 | Blackman, // blackman 78 | Blackman_Nuttall, // blackman nuttall 79 | Blackman_Harris, // blackman harris 80 | Flat_top, // flat top 81 | Welch // welch 82 | }; 83 | #define FFT_LIB_REV 0x15 84 | /* Custom constants */ 85 | #define FFT_FORWARD FFTDirection::Forward 86 | #define FFT_REVERSE FFTDirection::Reverse 87 | 88 | /* Windowing type */ 89 | #define FFT_WIN_TYP_RECTANGLE FFTWindow::Rectangle /* rectangle (Box car) */ 90 | #define FFT_WIN_TYP_HAMMING FFTWindow::Hamming /* hamming */ 91 | #define FFT_WIN_TYP_HANN FFTWindow::Hann /* hann */ 92 | #define FFT_WIN_TYP_TRIANGLE FFTWindow::Triangle /* triangle (Bartlett) */ 93 | #define FFT_WIN_TYP_NUTTALL FFTWindow::Nuttall /* nuttall */ 94 | #define FFT_WIN_TYP_BLACKMAN FFTWindow::Blackman /* blackman */ 95 | #define FFT_WIN_TYP_BLACKMAN_NUTTALL \ 96 | FFTWindow::Blackman_Nuttall /* blackman nuttall */ 97 | #define FFT_WIN_TYP_BLACKMAN_HARRIS \ 98 | FFTWindow::Blackman_Harris /* blackman harris*/ 99 | #define FFT_WIN_TYP_FLT_TOP FFTWindow::Flat_top /* flat top */ 100 | #define FFT_WIN_TYP_WELCH FFTWindow::Welch /* welch */ 101 | /*Mathematial constants*/ 102 | #define twoPi 6.28318531 103 | #define fourPi 12.56637061 104 | #define sixPi 18.84955593 105 | 106 | #ifdef __AVR__ 107 | static const FTYPE _c1[] PROGMEM = { 108 | 0.0000000000, 0.7071067812, 0.9238795325, 0.9807852804, 0.9951847267, 109 | 0.9987954562, 0.9996988187, 0.9999247018, 0.9999811753, 0.9999952938, 110 | 0.9999988235, 0.9999997059, 0.9999999265, 0.9999999816, 0.9999999954, 111 | 0.9999999989, 0.9999999997}; 112 | static const FTYPE _c2[] PROGMEM = { 113 | 1.0000000000, 0.7071067812, 0.3826834324, 0.1950903220, 0.0980171403, 114 | 0.0490676743, 0.0245412285, 0.0122715383, 0.0061358846, 0.0030679568, 115 | 0.0015339802, 0.0007669903, 0.0003834952, 0.0001917476, 0.0000958738, 116 | 0.0000479369, 0.0000239684}; 117 | #endif 118 | class arduinoFFT { 119 | public: 120 | /* Constructor */ 121 | arduinoFFT(void); 122 | arduinoFFT(FTYPE *vReal, FTYPE *vImag, uint16_t samples, 123 | FTYPE samplingFrequency); 124 | /* Destructor */ 125 | ~arduinoFFT(void); 126 | /* Functions */ 127 | uint8_t Revision(void); 128 | uint8_t Exponent(uint16_t value); 129 | 130 | void ComplexToMagnitude(FTYPE *vReal, FTYPE *vImag, uint16_t samples); 131 | void Compute(FTYPE *vReal, FTYPE *vImag, uint16_t samples, 132 | FFTDirection dir); 133 | void Compute(FTYPE *vReal, FTYPE *vImag, uint16_t samples, uint8_t power, 134 | FFTDirection dir); 135 | void DCRemoval(FTYPE *vData, uint16_t samples); 136 | FTYPE MajorPeak(FTYPE *vD, uint16_t samples, FTYPE samplingFrequency); 137 | void MajorPeak(FTYPE *vD, uint16_t samples, FTYPE samplingFrequency, 138 | FTYPE *f, FTYPE *v); 139 | void Windowing(FTYPE *vData, uint16_t samples, FFTWindow windowType, 140 | FFTDirection dir); 141 | 142 | void ComplexToMagnitude(); 143 | void Compute(FFTDirection dir); 144 | void DCRemoval(); 145 | FTYPE MajorPeak(); 146 | void MajorPeak(FTYPE *f, FTYPE *v); 147 | void Windowing(FFTWindow windowType, FFTDirection dir); 148 | 149 | FTYPE MajorPeakParabola(); 150 | 151 | private: 152 | /* Variables */ 153 | uint16_t _samples; 154 | FTYPE _samplingFrequency; 155 | FTYPE *_vReal; 156 | FTYPE *_vImag; 157 | uint8_t _power; 158 | /* Functions */ 159 | void Swap(FTYPE *x, FTYPE *y); 160 | void Parabola(FTYPE x1, FTYPE y1, FTYPE x2, FTYPE y2, FTYPE x3, 161 | FTYPE y3, FTYPE *a, FTYPE *b, FTYPE *c); 162 | }; 163 | 164 | #endif 165 | 166 | -------------------------------------------------------------------------------- /src/sid_settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Settings handling 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #ifndef _SID_SETTINGS_H 53 | #define _SID_SETTINGS_H 54 | 55 | extern bool haveSD; 56 | extern bool FlashROMode; 57 | 58 | extern uint8_t musFolderNum; 59 | 60 | #define MS(s) XMS(s) 61 | #define XMS(s) #s 62 | 63 | // Default settings 64 | 65 | #define DEF_HOSTNAME "sid" 66 | #define DEF_WIFI_RETRY 3 // 1-10; Default: 3 retries 67 | #define DEF_WIFI_TIMEOUT 7 // 7-25; Default: 7 seconds 68 | #define DEF_AP_CHANNEL 1 // 1-13; 0 = random(1-13) 69 | #define DEF_WIFI_APOFFDELAY 0 70 | 71 | #define DEF_STRICT 1 // 0: Allow random diviations from movie patterns; 1: no not 72 | #define DEF_SKIP_TTANIM 1 // 0: Don't skip tt anim; 1: do 73 | #define DEF_BOOTSA 0 // 0: Boot into IDLE, 1: Boot into Spectrum Analyzer 74 | #define DEF_SA_PEAKS 0 // 1: Show peaks in SA, 0: don't 75 | #define DEF_SS_TIMER 0 // "Screen saver" timeout in minutes; 0 = ss off 76 | 77 | #define DEF_TCD_IP "" // TCD ip address or hostname for BTTFN 78 | #define DEF_USE_GPSS 0 // 0: Ignore GPS speed; 1: Use it for chase speed 79 | #define DEF_USE_NM 0 // 0: Ignore TCD night mode; 1: Follow TCD night mode 80 | #define DEF_USE_FPO 0 // 0: Ignore TCD fake power; 1: Follow TCD fake power 81 | #define DEF_BTTFN_TT 1 // 0: '0' on IR remote and TT button trigger stand-alone TT; 1: They trigger BTTFN-wide TT 82 | #define DEF_SS_CLK 0 // "Screen saver" is clock (0=off, 1=on) 83 | #define DEF_SS_CLK_NMOFF 0 // 0: Clock dimmed in NM 1: Clock off in NM 84 | 85 | #define DEF_TCD_PRES 0 // 0: No TCD connected, 1: connected via GPIO 86 | #define DEF_NO_ETTO_LEAD 0 // Default: 0: TCD signals TT with ETTO_LEAD lead time; 1 without 87 | 88 | #define DEF_CFG_ON_SD 1 // Default: Save secondary settings on SD card 89 | #define DEF_SD_FREQ 0 // SD/SPI frequency: Default 16MHz 90 | 91 | #define DEF_DISDIR 0 // 0: Do not disable default IR remote control; 1: do 92 | 93 | struct Settings { 94 | char ssid[34] = ""; 95 | char pass[66] = ""; 96 | 97 | char hostName[32] = DEF_HOSTNAME; 98 | char wifiConRetries[4] = MS(DEF_WIFI_RETRY); 99 | char wifiConTimeout[4] = MS(DEF_WIFI_TIMEOUT); 100 | char systemID[8] = ""; 101 | char appw[10] = ""; 102 | char apChnl[4] = MS(DEF_AP_CHANNEL); 103 | char wifiAPOffDelay[4] = MS(DEF_WIFI_APOFFDELAY); 104 | 105 | char strictMode[4] = MS(DEF_STRICT); // saved, but overruled by idlePat config file 106 | char skipTTAnim[4] = MS(DEF_SKIP_TTANIM); 107 | char bootSA[4] = MS(DEF_BOOTSA); 108 | char SApeaks[4] = MS(DEF_SA_PEAKS); 109 | char ssTimer[6] = MS(DEF_SS_TIMER); 110 | 111 | char tcdIP[32] = DEF_TCD_IP; 112 | char useGPSS[4] = MS(DEF_USE_GPSS); 113 | char useNM[4] = MS(DEF_USE_NM); 114 | char useFPO[4] = MS(DEF_USE_FPO); 115 | char bttfnTT[4] = MS(DEF_BTTFN_TT); 116 | char ssClock[4] = MS(DEF_SS_CLK); 117 | char ssClockOffNM[4] = MS(DEF_SS_CLK_NMOFF); 118 | 119 | #ifdef SID_HAVEMQTT 120 | char useMQTT[4] = "0"; 121 | char mqttVers[4] = "0"; // 0 = 3.1.1, 1 = 5.0 122 | char mqttServer[80] = ""; // ip or domain [:port] 123 | char mqttUser[128] = ""; // user[:pass] (UTF8) 124 | #endif 125 | 126 | char TCDpresent[4] = MS(DEF_TCD_PRES); 127 | char noETTOLead[4] = MS(DEF_NO_ETTO_LEAD); 128 | 129 | char CfgOnSD[4] = MS(DEF_CFG_ON_SD); 130 | char sdFreq[4] = MS(DEF_SD_FREQ); 131 | 132 | char disDIR[4] = MS(DEF_DISDIR); 133 | }; 134 | 135 | struct IPSettings { 136 | char ip[20] = ""; 137 | char gateway[20] = ""; 138 | char netmask[20] = ""; 139 | char dns[20] = ""; 140 | }; 141 | 142 | extern struct Settings settings; 143 | extern struct IPSettings ipsettings; 144 | 145 | void settings_setup(); 146 | 147 | void unmount_fs(); 148 | 149 | void write_settings(); 150 | bool checkConfigExists(); 151 | 152 | bool loadBrightness(); 153 | void saveBrightness(bool useCache = true); 154 | 155 | bool loadIdlePat(); 156 | void saveIdlePat(bool useCache = true); 157 | 158 | bool loadIRLock(); 159 | void saveIRLock(bool useCache = true); 160 | 161 | bool saveIRKeys(); 162 | void deleteIRKeys(); 163 | 164 | bool loadIpSettings(); 165 | void writeIpSettings(); 166 | void deleteIpSettings(); 167 | 168 | void copySettings(); 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /src/mqtt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * PubSubClient.h - A simple client for MQTT. 4 | * Nick O'Leary 5 | * http://knolleary.net 6 | * Minimized & adapted by Thomas Winischhofer (A10001986) in 2023 7 | * MQTT 5.0 support by Thomas Winischhofer (A10001986) in 2025 8 | * 9 | * Copyright (c) 2008-2020 Nicholas O'Leary 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files (the 13 | * "Software"), to deal in the Software without restriction, including 14 | * without limitation the rights to use, copy, modify, merge, publish, 15 | * distribute, sublicense, and/or sell copies of the Software, and to 16 | * permit persons to whom the Software is furnished to do so, subject to 17 | * the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * In addition, the following restrictions apply: 23 | * 24 | * 1. The Software and any modifications made to it may not be used 25 | * for the purpose of training or improving machine learning algorithms, 26 | * including but not limited to artificial intelligence, natural 27 | * language processing, or data mining. This condition applies to any 28 | * derivatives, modifications, or updates based on the Software code. 29 | * Any usage of the Software in an AI-training dataset is considered a 30 | * breach of this License. 31 | * 32 | * 2. The Software may not be included in any dataset used for 33 | * training or improving machine learning algorithms, including but 34 | * not limited to artificial intelligence, natural language processing, 35 | * or data mining. 36 | * 37 | * 3. Any person or organization found to be in violation of these 38 | * restrictions will be subject to legal action and may be held liable 39 | * for any damages resulting from such use. 40 | * 41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 44 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 45 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 46 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 47 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | * 49 | */ 50 | 51 | #ifndef PubSubClient_h 52 | #define PubSubClient_h 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | #define MQTT_VERSION_3_1_1 4 59 | #define MQTT_VERSION_5_0 5 60 | 61 | // MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize(). 62 | #ifndef MQTT_MAX_PACKET_SIZE 63 | #define MQTT_MAX_PACKET_SIZE 512 64 | #endif 65 | 66 | // MQTT_KEEPALIVE : keepAlive interval in Seconds. 67 | #ifndef MQTT_KEEPALIVE 68 | #define MQTT_KEEPALIVE 15 69 | #endif 70 | 71 | // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. 72 | #ifndef MQTT_SOCKET_TIMEOUT 73 | #define MQTT_SOCKET_TIMEOUT 15 74 | #endif 75 | 76 | // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client 77 | // in each write call. Needed for the Arduino Wifi Shield. Leave undefined to 78 | // pass the entire MQTT packet in each write call. 79 | //#define MQTT_MAX_TRANSFER_SIZE 80 80 | 81 | // Possible values for client.state() 82 | #define MQTT_CONNECTING -5 83 | #define MQTT_CONNECTION_TIMEOUT -4 84 | #define MQTT_CONNECTION_LOST -3 85 | #define MQTT_CONNECT_FAILED -2 86 | #define MQTT_DISCONNECTED -1 87 | #define MQTT_CONNECTED 0 88 | #define MQTT_CONNECT_BAD_PROTOCOL 1 // v3 89 | #define MQTT_CONNECT_BAD_CLIENT_ID 2 // v3 90 | #define MQTT_CONNECT_UNAVAILABLE 3 // v3 91 | #define MQTT_CONNECT_BAD_CREDENTIALS 4 // v3 92 | #define MQTT_CONNECT_UNAUTHORIZED 5 // v3 93 | 94 | #define MQTTCONNECT 1 << 4 // Client request to connect to Server 95 | #define MQTTCONNACK 2 << 4 // Connect Acknowledgment 96 | #define MQTTPUBLISH 3 << 4 // Publish message 97 | #define MQTTPUBACK 4 << 4 // Publish Acknowledgment 98 | #define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1) 99 | #define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2) 100 | #define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3) 101 | #define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request 102 | #define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment 103 | #define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request 104 | #define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment 105 | #define MQTTPINGREQ 12 << 4 // PING Request 106 | #define MQTTPINGRESP 13 << 4 // PING Response 107 | #define MQTTDISCONNECT 14 << 4 // Client is Disconnecting 108 | #define MQTTReserved 15 << 4 // Reserved 109 | 110 | #define MQTTQOS0 (0 << 1) 111 | #define MQTTQOS1 (1 << 1) 112 | #define MQTTQOS2 (2 << 1) 113 | 114 | // Maximum size of fixed header (header byte + variable length field) 115 | #define MQTT_MAX_HEADER_SIZE_3_1_1 5 116 | #define MQTT_MAX_HEADER_SIZE_5_0 5 117 | 118 | #define PING_ERROR -1 119 | #define PING_IDLE 0 120 | #define PING_PINGING 1 121 | 122 | #define CHECK_STRING_LENGTH(l,s) if(l+2+strnlen(s, this->bufferSize) > this->bufferSize) { _client->stop(); return false; } 123 | 124 | class PubSubClient { 125 | 126 | public: 127 | PubSubClient(WiFiClient& client); 128 | 129 | ~PubSubClient(); 130 | 131 | void setClientID(const char *src); 132 | bool setBufferSize(uint16_t size); 133 | void setVersion(int mqtt_version); 134 | 135 | void setServer(IPAddress ip, uint16_t port) { this->ip = ip; this->port = port; this->domain = NULL; } 136 | void setServer(const char *domain, uint16_t port) { this->domain = domain; this->port = port; } 137 | void setCallback(void (*callback)(char *, uint8_t *, unsigned int)) { this->callback = callback; } 138 | void setLooper(void (*looper)()) { this->looper = looper; } 139 | 140 | bool connect(); 141 | bool connect(const char *user, const char *pass); 142 | bool connect(const char *user, const char *pass, bool cleanSession); 143 | bool connected(); 144 | int state() { return this->_state; } 145 | 146 | bool loop(); 147 | 148 | bool publish(const char *topic, const uint8_t *payload, unsigned int plength, bool retained = false); 149 | 150 | bool subscribe(const char *topic, const char *topic2 = NULL, uint8_t qos = 0); 151 | bool unsubscribe(const char *topic); 152 | 153 | void disconnect(); 154 | 155 | bool sendPing(); 156 | bool pollPing(); 157 | void cancelPing(); 158 | int pstate() { return this->_pstate; } 159 | 160 | private: 161 | 162 | bool subscribe_int(bool unsubscribe, const char *topic, const char *topic2L, uint8_t qos); 163 | 164 | uint32_t readPacket(uint8_t *); 165 | bool readByte(uint8_t *result); 166 | bool readByte(uint8_t *result, uint16_t *index); 167 | 168 | size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); 169 | bool write(uint8_t header, uint8_t *buf, uint16_t length); 170 | 171 | int _vbl(const uint8_t *buf, unsigned int& length); 172 | int _searchProp(uint8_t *buf, uint8_t prop, const int propLength); 173 | 174 | uint16_t writeString(const char *string, uint8_t *buf, uint16_t pos); 175 | 176 | WiFiClient* _client; 177 | uint8_t* buffer; 178 | uint16_t bufferSize; 179 | uint16_t keepAlive; 180 | unsigned long socketTimeout; 181 | uint16_t nextMsgId; 182 | unsigned long lastOutActivity; 183 | unsigned long lastInActivity; 184 | bool pingOutstanding; 185 | void (*callback)(char *, uint8_t *, unsigned int); 186 | void (*looper)(); 187 | 188 | IPAddress ip; 189 | const char* domain; 190 | uint16_t port; 191 | int _state; 192 | 193 | int _s; 194 | int _pstate = PING_IDLE; 195 | uint16_t _pseq_num = 34; 196 | 197 | bool _v3 = true; 198 | 199 | uint16_t mqtt_max_header_size = MQTT_MAX_HEADER_SIZE_3_1_1; 200 | int mqtt_version_header_length = 7; 201 | uint8_t _phdr[7] = { 0x00, 0x04, 'M', 'Q', 'T', 'T', MQTT_VERSION_3_1_1 }; 202 | 203 | char _clientID[24]; 204 | }; 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /src/sid_snake.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Snake 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #include "sid_global.h" 53 | 54 | #include 55 | 56 | #include "sid_snake.h" 57 | #include "sid_main.h" 58 | 59 | #define WIDTH 10 60 | #define HEIGHT 20 61 | 62 | #define MAXLENGTH 100 63 | 64 | #define NUM_LEVELS 9 65 | #define APPLES_PER_LEVEL 15 66 | 67 | bool snActive = false; 68 | 69 | static int spx = 0; 70 | static int spy = 0; 71 | static int sdx = 0; 72 | static int sdy = 0; 73 | static int scl = 0; 74 | static int sml = 0; 75 | static int snake[MAXLENGTH][2] = { { 0, 0 } }; 76 | 77 | static int apx = 0; 78 | static int apy = 0; 79 | 80 | static const unsigned long ldelays[NUM_LEVELS] = { 81 | 700, 600, 550, 500, 450, 400, 300, 200, 100 82 | }; 83 | 84 | static unsigned long cp_now = 0; 85 | 86 | static unsigned long snStartup = 0; // startup sequence running 87 | static bool gameOver = false; 88 | static bool gameOverShown = false; 89 | static bool pauseGame = false; 90 | static bool pauseShown = false; 91 | static int level = 0; // current level (speed) 92 | static int acnt = 0; // apple count in level 93 | 94 | static void updateDisplay() 95 | { 96 | uint8_t myField[WIDTH * HEIGHT] = { 0 }; 97 | 98 | // Snake 99 | for(int i = 0; i < scl - 1; i++) { 100 | myField[(snake[i][1] * WIDTH) + snake[i][0]] = 1; 101 | } 102 | 103 | // Apple 104 | if(apx >= 0) { 105 | myField[(apy * WIDTH) + apx] = 1; 106 | } 107 | 108 | sid.drawFieldAndShow((uint8_t *)myField); 109 | } 110 | 111 | static void shiftSnake() 112 | { 113 | for(int i = MAXLENGTH - 2; i >= 0; i--) { 114 | snake[i+1][0] = snake[i][0]; 115 | snake[i+1][1] = snake[i][1]; 116 | } 117 | } 118 | 119 | static bool appleHitsSnake() 120 | { 121 | for(int i = 0; i < scl; i++) { 122 | if(apx == snake[i][0] && apy == snake[i][1]) 123 | return true; 124 | } 125 | 126 | return false; 127 | } 128 | 129 | static void resetGame() 130 | { 131 | spx = WIDTH / 2; // pos of head 132 | spy = HEIGHT / 2; 133 | sdx = 1; // movement deltas 134 | sdy = 0; 135 | scl = 4; // current length incl head 136 | 137 | for(int i = 0; i < scl; i++) { 138 | snake[i][0] = spx - i; 139 | snake[i][1] = spy; 140 | } 141 | 142 | apx = WIDTH / 4; // apple position 143 | apy = HEIGHT / 4; 144 | 145 | acnt = 1; 146 | 147 | gameOver = gameOverShown = false; 148 | pauseGame = pauseShown = false; 149 | } 150 | 151 | void sn_init() 152 | { 153 | resetGame(); 154 | level = 0; 155 | 156 | showWordSequence("SNAKE", 2); 157 | 158 | snStartup = millis(); 159 | snActive = true; 160 | } 161 | 162 | void sn_loop() 163 | { 164 | unsigned long now = millis(); 165 | bool skipCheck = false; 166 | bool newApple = false; 167 | 168 | if(!snActive) 169 | return; 170 | 171 | if(gameOver) { 172 | if(!gameOverShown) { 173 | showWordSequence("GAME OVER ", 1); 174 | gameOverShown = true; 175 | return; 176 | } 177 | resetGame(); 178 | level = 0; 179 | updateDisplay(); 180 | cp_now = now; 181 | return; 182 | } 183 | 184 | if(snStartup) { 185 | if(now - snStartup < 1000) { 186 | return; 187 | } 188 | snStartup = 0; 189 | updateDisplay(); 190 | cp_now = now; 191 | return; 192 | } 193 | 194 | if(pauseGame) { 195 | if(!pauseShown) { 196 | sid.drawLetterAndShow('P'); 197 | pauseShown = true; 198 | } 199 | return; 200 | } 201 | 202 | if(now - cp_now < ldelays[level]) 203 | return; 204 | 205 | // Move snake 206 | spx += sdx; 207 | spy += sdy; 208 | 209 | // Wrap snake 210 | if(spx < 0) spx = WIDTH - 1; 211 | if(spx >= WIDTH) spx = 0; 212 | if(spy < 0) spy = HEIGHT - 1; 213 | if(spy >= HEIGHT) spy = 0; 214 | 215 | // Update snake[] 216 | shiftSnake(); 217 | snake[0][0] = spx; 218 | snake[0][1] = spy; 219 | 220 | // Check if head hits apple 221 | if(spx == apx && spy == apy) { 222 | scl++; 223 | acnt++; 224 | if(scl >= MAXLENGTH || acnt > APPLES_PER_LEVEL) { 225 | char lvl[2]; 226 | resetGame(); 227 | if(level < NUM_LEVELS - 1) { 228 | char lvl[2]; 229 | level++; 230 | lvl[0] = level + 1 + '0'; 231 | lvl[1] = 0; 232 | showWordSequence(lvl, 3); 233 | } else { 234 | // User won all levels; restart for now 235 | level = 0; 236 | } 237 | skipCheck = true; 238 | } else 239 | newApple = true; 240 | } 241 | 242 | // Make new apply 243 | if(newApple) { 244 | do { 245 | apx = esp_random() % WIDTH; 246 | apy = esp_random() % HEIGHT; 247 | } while(appleHitsSnake()); 248 | } 249 | 250 | // Check if any body parts of snake collide -> game over 251 | if(!skipCheck) { 252 | for(int i = 0; i < scl; i++) { 253 | int cx = snake[i][0], cy = snake[i][1]; 254 | for(int j = i + 1; j < scl; j++) { 255 | if(snake[j][0] == cx && snake[j][1] == cy) { 256 | gameOver = true; 257 | } 258 | } 259 | } 260 | } 261 | 262 | cp_now = now; 263 | updateDisplay(); 264 | } 265 | 266 | void sn_end() 267 | { 268 | if(!snActive) 269 | return; 270 | 271 | snActive = false; 272 | } 273 | 274 | void sn_newGame() 275 | { 276 | if(!snActive || snStartup) 277 | return; 278 | 279 | resetGame(); 280 | level = 0; 281 | updateDisplay(); 282 | } 283 | 284 | void sn_pause() 285 | { 286 | if(!snActive || gameOver || snStartup) 287 | return; 288 | 289 | pauseGame = !pauseGame; 290 | pauseShown = false; 291 | } 292 | 293 | void sn_moveRight() // move right 294 | { 295 | if(!snActive || gameOver || snStartup || pauseGame) 296 | return; 297 | 298 | sdx = 1; 299 | sdy = 0; 300 | } 301 | 302 | void sn_moveLeft() // move left 303 | { 304 | if(!snActive || gameOver || snStartup || pauseGame) 305 | return; 306 | 307 | sdx = -1; 308 | sdy = 0; 309 | } 310 | 311 | void sn_moveUp() // move down 312 | { 313 | if(!snActive || gameOver || snStartup || pauseGame) 314 | return; 315 | 316 | sdx = 0; 317 | sdy = -1; 318 | } 319 | 320 | void sn_moveDown() // move down 321 | { 322 | if(!snActive || gameOver || snStartup || pauseGame) 323 | return; 324 | 325 | sdx = 0; 326 | sdy = 1; 327 | } 328 | -------------------------------------------------------------------------------- /src/input.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * FCRemote Class: Remote control handling 9 | * Inspired by Ken Shirriff's IRRemote library 10 | * 11 | * ------------------------------------------------------------------- 12 | * License: MIT NON-AI 13 | * 14 | * Permission is hereby granted, free of charge, to any person 15 | * obtaining a copy of this software and associated documentation 16 | * files (the "Software"), to deal in the Software without restriction, 17 | * including without limitation the rights to use, copy, modify, 18 | * merge, publish, distribute, sublicense, and/or sell copies of the 19 | * Software, and to permit persons to whom the Software is furnished to 20 | * do so, subject to the following conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be 23 | * included in all copies or substantial portions of the Software. 24 | * 25 | * In addition, the following restrictions apply: 26 | * 27 | * 1. The Software and any modifications made to it may not be used 28 | * for the purpose of training or improving machine learning algorithms, 29 | * including but not limited to artificial intelligence, natural 30 | * language processing, or data mining. This condition applies to any 31 | * derivatives, modifications, or updates based on the Software code. 32 | * Any usage of the Software in an AI-training dataset is considered a 33 | * breach of this License. 34 | * 35 | * 2. The Software may not be included in any dataset used for 36 | * training or improving machine learning algorithms, including but 37 | * not limited to artificial intelligence, natural language processing, 38 | * or data mining. 39 | * 40 | * 3. Any person or organization found to be in violation of these 41 | * restrictions will be subject to legal action and may be held liable 42 | * for any damages resulting from such use. 43 | * 44 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 47 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 48 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 49 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 50 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 51 | */ 52 | 53 | #include 54 | 55 | #include "input.h" 56 | 57 | /* 58 | * IRRemote class 59 | */ 60 | 61 | #define TMR_TIME 0.00005 // 0.00005s = 50us 62 | #define TMR_PRESCALE 80 63 | #define TMR_TICKS (uint64_t)(((double)TMR_TIME * 80000000.0) / (double)TMR_PRESCALE) 64 | #define TME_TIMEUS (TMR_TIME * 1000000) 65 | 66 | #define GAP_DUR 5000 // Minimum gap between transmissions in us (microseconds) 67 | #define GAP_TICKS (GAP_DUR / TME_TIMEUS) 68 | 69 | // IR receiver pin polarity 70 | #define IR_LIGHT 0 71 | #define IR_DARK 1 72 | 73 | static void IRAM_ATTR IRTimer_ISR(); 74 | 75 | static uint8_t _ir_pin; 76 | 77 | static volatile uint32_t _cnt = 0; 78 | static volatile IRState _irstate = IRSTATE_IDLE; 79 | static volatile uint32_t _irlen = 0; 80 | static volatile uint32_t _irbuf[IRBUFSIZE]; 81 | 82 | // ISR 83 | // Record duration of marks/spaces through a simple state machine 84 | static void IRAM_ATTR IRTimer_ISR() 85 | { 86 | uint8_t irpin = (uint8_t)digitalRead(_ir_pin); 87 | 88 | _cnt++; 89 | 90 | switch(_irstate) { 91 | case IRSTATE_IDLE: 92 | if(irpin == IR_LIGHT) { 93 | if(_cnt >= GAP_TICKS) { 94 | // Current gap longer than minimum gap size, 95 | // start recording. 96 | // (In case of a smaller gap, we assume being in 97 | // the middle of a transmission whose start we 98 | // missed. Do nothing then. 99 | _irstate = IRSTATE_LIGHT; 100 | _irbuf[0] = _cnt; // First is length of previous gap 101 | _irlen = 1; 102 | } 103 | _cnt = 0; 104 | } 105 | break; 106 | case IRSTATE_LIGHT: 107 | if(irpin == IR_DARK) { 108 | _irstate = IRSTATE_DARK; 109 | _irbuf[_irlen++] = _cnt; 110 | _cnt = 0; 111 | if(_irlen >= IRBUFSIZE) _irstate = IRSTATE_STOP; 112 | } 113 | break; 114 | case IRSTATE_DARK: 115 | if(irpin == IR_LIGHT) { 116 | _irstate = IRSTATE_LIGHT; 117 | _irbuf[_irlen++] = _cnt; 118 | _cnt = 0; 119 | if(_irlen >= IRBUFSIZE) _irstate = IRSTATE_STOP; 120 | } else if(_cnt > GAP_TICKS) { 121 | // Gap longer than usual space, transmission finished. 122 | _irstate = IRSTATE_STOP; 123 | } 124 | break; 125 | case IRSTATE_STOP: 126 | if(irpin == IR_LIGHT) _cnt = 0; // Reset cnt whenever we see something, even if we miss recording it 127 | break; 128 | } 129 | } 130 | 131 | // Store basic config data 132 | IRRemote::IRRemote(uint8_t timer_no, uint8_t ir_pin) 133 | { 134 | _timer_no = timer_no; 135 | _ir_pin = ir_pin; 136 | } 137 | 138 | void IRRemote::begin() 139 | { 140 | pinMode(_ir_pin, INPUT); 141 | _irstate = IRSTATE_IDLE; 142 | _irlen = 0; 143 | 144 | // Install & enable interrupt 145 | _IRTimer = timerBegin(_timer_no, TMR_PRESCALE, true); 146 | timerAttachInterrupt(_IRTimer, &IRTimer_ISR, true); 147 | timerAlarmWrite(_IRTimer, TMR_TICKS, true); 148 | timerAlarmEnable(_IRTimer); 149 | } 150 | 151 | // Decode IR signal 152 | bool IRRemote::loop() 153 | { 154 | // No new transmission, bail... 155 | if(_irstate != IRSTATE_STOP) 156 | return false; 157 | 158 | // Copy result to backup buffer 159 | _buflen = _irlen; 160 | for(uint8_t i = 0; i < _buflen; i++) { 161 | _buf[i] = _irbuf[i]; 162 | } 163 | 164 | // Continue recording 165 | resume(); 166 | 167 | // Calc hash on received "code" 168 | if(calcHash()) { 169 | //unsigned long now = millis(); 170 | // Repeat-key-avoidance hinders game play! 171 | //if(_hvalue == _prevHash) { 172 | // if(now - _prevTime < 300) { 173 | // _prevTime = now; 174 | // return false; 175 | // } 176 | //} 177 | _prevHash = _hvalue; 178 | //_prevTime = now; 179 | return true; 180 | } 181 | 182 | return false; 183 | } 184 | 185 | void IRRemote::resume() 186 | { 187 | _irstate = IRSTATE_IDLE; 188 | } 189 | 190 | uint32_t IRRemote::readHash() 191 | { 192 | return _hvalue; 193 | } 194 | 195 | 196 | /* CalcHash: Calculate hash over an arbitrary IR code 197 | * 198 | * Based on code published here: 199 | * http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html 200 | * 201 | */ 202 | 203 | uint32_t IRRemote::compare(uint32_t a, uint32_t b) 204 | { 205 | if(b < a * 80 / 100) return 0; 206 | if(a < b * 80 / 100) return 2; 207 | return 1; 208 | } 209 | 210 | /* Converts the raw code values into a 32-bit hash code. 211 | * Use FNV-1 (Fowler–Noll–Vo) hash algorithm. 212 | * https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function 213 | */ 214 | 215 | #define FNV_PRIME_32 16777619 216 | #define FNV_BASIS_32 2166136261 217 | 218 | bool IRRemote::calcHash() 219 | { 220 | if(_buflen < 6) 221 | return false; 222 | 223 | uint32_t hash = FNV_BASIS_32; 224 | 225 | for(int i = 1; i + 2 < _buflen; i++) { 226 | hash = (hash * FNV_PRIME_32) ^ compare(_buf[i], _buf[i+2]); 227 | } 228 | 229 | _hvalue = hash; 230 | 231 | return true; 232 | } 233 | 234 | /* 235 | * SIDButton class 236 | * 237 | * If a Long-Press-function is registered, a "press" is only reported only after 238 | * the button is released. If no such function is registered, a press is 239 | * reported immediately (after PressTicks have elapsed), regardless of a button 240 | * release. The latter mode is used for when the TCD is connected to trigger 241 | * time travels. 242 | */ 243 | 244 | /* pin: The pin to be used 245 | * activeLow: Set to true when the input level is LOW when the button is pressed, Default is true. 246 | * pullupActive: Activate the internal pullup when available. Default is true. 247 | */ 248 | SIDButton::SIDButton(const int pin, const bool activeLow, const bool pullupActive, const bool pulldownActive) 249 | { 250 | _pin = pin; 251 | 252 | _buttonPressed = activeLow ? LOW : HIGH; 253 | 254 | pinMode(pin, pullupActive ? INPUT_PULLUP : (pulldownActive ? INPUT_PULLDOWN : INPUT)); 255 | } 256 | 257 | // Setup buttom timin: 258 | // dticks: Number of millisec for a stable click to be assumed 259 | // pticks: Number of millisec to pass for a short press 260 | // lticks: Number of millisec to pass for a long press 261 | void SIDButton::setTiming(const int debounceDur, const int pressDur, const int lPressDur) 262 | { 263 | _debounceDur = debounceDur; 264 | _pressDur = pressDur; 265 | _longPressDur = lPressDur; 266 | } 267 | 268 | // Register function for short press event 269 | void SIDButton::attachPress(void (*newFunction)(void)) 270 | { 271 | _pressFunc = newFunction; 272 | } 273 | 274 | // Register function for long press start event 275 | void SIDButton::attachLongPressStart(void (*newFunction)(void)) 276 | { 277 | _longPressStartFunc = newFunction; 278 | } 279 | 280 | // Register function for long press stop event 281 | void SIDButton::attachLongPressStop(void (*newFunction)(void)) 282 | { 283 | _longPressStopFunc = newFunction; 284 | } 285 | 286 | // Check input of the pin and advance the state machine 287 | void SIDButton::scan(void) 288 | { 289 | unsigned long now = millis(); 290 | unsigned long waitTime = now - _startTime; 291 | bool active = (digitalRead(_pin) == _buttonPressed); 292 | 293 | switch(_state) { 294 | case TCBS_IDLE: 295 | if(active) { 296 | transitionTo(TCBS_PRESSED); 297 | _startTime = now; 298 | } 299 | break; 300 | 301 | case TCBS_PRESSED: 302 | if((!active) && (waitTime < _debounceDur)) { // de-bounce 303 | transitionTo(_lastState); 304 | } else if(!active) { 305 | transitionTo(TCBS_RELEASED); 306 | _startTime = now; 307 | } else { 308 | if(!_longPressStartFunc) { 309 | if(waitTime > _pressDur) { 310 | if(_pressFunc) _pressFunc(); 311 | _pressNotified = true; 312 | } 313 | } else if(waitTime > _longPressDur) { 314 | if(_longPressStartFunc) _longPressStartFunc(); 315 | transitionTo(TCBS_LONGPRESS); 316 | } 317 | } 318 | break; 319 | 320 | case TCBS_RELEASED: 321 | if((active) && (waitTime < _debounceDur)) { // de-bounce 322 | transitionTo(_lastState); 323 | } else if((!active) && (waitTime > _pressDur)) { 324 | if(!_pressNotified && _pressFunc) _pressFunc(); 325 | reset(); 326 | } 327 | break; 328 | 329 | case TCBS_LONGPRESS: 330 | if(!active) { 331 | transitionTo(TCBS_LONGPRESSEND); 332 | _startTime = now; 333 | } 334 | break; 335 | 336 | case TCBS_LONGPRESSEND: 337 | if((active) && (waitTime < _debounceDur)) { // de-bounce 338 | transitionTo(_lastState); 339 | } else if(waitTime >= _debounceDur) { 340 | if(_longPressStopFunc) _longPressStopFunc(); 341 | reset(); 342 | } 343 | break; 344 | 345 | default: 346 | transitionTo(TCBS_IDLE); 347 | break; 348 | } 349 | } 350 | 351 | /* 352 | * Private 353 | */ 354 | 355 | void SIDButton::reset(void) 356 | { 357 | _state = TCBS_IDLE; 358 | _lastState = TCBS_IDLE; 359 | _startTime = 0; 360 | _pressNotified = false; 361 | } 362 | 363 | // Advance to new state 364 | void SIDButton::transitionTo(ButtonState nextState) 365 | { 366 | _lastState = _state; 367 | _state = nextState; 368 | } 369 | 370 | -------------------------------------------------------------------------------- /src/sid_sa.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Spectrum Analyzer 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #include "sid_global.h" 53 | #include 54 | #include "src/arduinoFFT/arduinoFFT.h" 55 | #include 56 | #include 57 | #include 58 | #include "sid_main.h" 59 | 60 | #define NUMBANDS 11 // Number of bands ("bins" in FFT-speak) 61 | #define DISPLAYBANDS 10 // Displayed number of bands 62 | #define LEDS_PER_BAR 20 // Height of bar 63 | 64 | #define NUMSAMPLES 1024 // Size of sample block 65 | #define SAMPLERATE 32000 // Sampling frequency 66 | 67 | #define PEAK_HOLD 500 // ms - Peak hold time 68 | #define PEAK_FALL 100 // ms - Peak fall speed 69 | 70 | //#define SA_DBG_WRITEOUT // For debugging 71 | 72 | static const i2s_port_t I2S_PORT = I2S_NUM_0; 73 | 74 | static int32_t rawSamples[NUMSAMPLES]; 75 | static FTYPE vReal[NUMSAMPLES]; 76 | static FTYPE vImag[NUMSAMPLES]; 77 | 78 | static FTYPE freqBands[NUMBANDS] = { 0 }; 79 | 80 | // 32 = 32ms * 32 = 1 sec 81 | // 64 = 32ms * 64 = 2 secs 82 | // 128 = 32ms * 128 = 4 secs 83 | #define FQ_HIST 128 84 | static int histIdx = 0; 85 | static FTYPE freqBandsHistory[FQ_HIST][NUMBANDS] = { 0 }; 86 | 87 | // The frequency bands 88 | // First one is "garbage bin", not used for display 89 | static const int freqSteps[NUMBANDS] = { 90 | 80, 100, 150, 250, 430, 600, 1000, 2000, 4000, 7000, 10000 91 | }; 92 | 93 | // Noise threshold per band. Lower bands have more noise. 94 | static const int minTreshold[NUMBANDS] = { 95 | 0, 5000, 5000, 5000, 3000, 1000, 1000, 1000, 1000, 1000, 1000 96 | }; 97 | 98 | static const uint8_t maxTTHeight[10] = { 99 | 20, 20, 13, 20, 20, 20, 20, 10, 20, 17 100 | }; 101 | 102 | static int oldHeight[DISPLAYBANDS] = { 0 }; 103 | 104 | static uint8_t peaks[DISPLAYBANDS] = { 0 }; 105 | static unsigned long newPeak[DISPLAYBANDS] = { 0 }; 106 | static unsigned long peakTimer[DISPLAYBANDS] = { 0 }; 107 | 108 | bool saActive = false; 109 | static bool sa_avail = false; 110 | bool doPeaks = false; 111 | static bool startFlag = false; 112 | static bool initFlag = false; 113 | static bool initDisplay = true; 114 | static unsigned long lastTime = 0; 115 | static unsigned long lastStart = 0; 116 | static unsigned long startDelay = 0; 117 | 118 | int ampFact = 100; 119 | 120 | #if defined(SID_DBG) && defined(SA_DBG_WRITEOUT) 121 | #include "sid_settings.h" 122 | #include 123 | #include 124 | static File outFile; 125 | static bool outFileOpen = false; 126 | #endif 127 | 128 | static const i2s_pin_config_t i2sPins = { 129 | .bck_io_num = I2S_BCLK_PIN, 130 | .ws_io_num = I2S_LRCLK_PIN, 131 | .data_out_num = I2S_PIN_NO_CHANGE, 132 | .data_in_num = I2S_DIN_PIN 133 | }; 134 | 135 | static const i2s_config_t i2s_config = { 136 | .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), 137 | .sample_rate = SAMPLERATE, 138 | .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, 139 | .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, 140 | .communication_format = I2S_COMM_FORMAT_STAND_MSB, 141 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, 142 | .dma_buf_count = 4, 143 | .dma_buf_len = 1024, 144 | .use_apll = false, 145 | .tx_desc_auto_clear = false, 146 | .fixed_mclk = 0 147 | }; 148 | 149 | static bool sa_setup() 150 | { 151 | esp_err_t err; 152 | 153 | if(sa_avail) 154 | return true; 155 | 156 | err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); 157 | if(err != ESP_OK) { 158 | #ifdef SID_DBG 159 | Serial.printf("sa_setup: Failed to install i2s driver (%d)\n", err); 160 | #endif 161 | return false; 162 | } 163 | 164 | // For SPH0645 165 | REG_SET_BIT(I2S_TIMING_REG(I2S_PORT), BIT(9)); 166 | REG_SET_BIT(I2S_CONF_REG(I2S_PORT), I2S_RX_MSB_SHIFT); 167 | 168 | i2s_set_pin(I2S_PORT, &i2sPins); 169 | 170 | sa_avail = true; 171 | 172 | #if defined(SID_DBG) && defined(SA_DBG_WRITEOUT) 173 | if(haveSD) { 174 | outFile = SD.open("/sidsa.pcm", FILE_WRITE); 175 | outFileOpen = true; 176 | } 177 | #endif 178 | 179 | return true; 180 | } 181 | 182 | #if 0 // Unused 183 | void sa_remove() 184 | { 185 | if(!sa_avail) 186 | return; 187 | 188 | i2s_driver_uninstall(I2S_PORT); 189 | sa_avail = false; 190 | } 191 | #endif 192 | 193 | // internal resume/stop 194 | 195 | static void sa_resume(bool initDisp, unsigned long start_Delay) 196 | { 197 | if(!sa_avail) 198 | sa_setup(); 199 | else 200 | i2s_start(I2S_PORT); 201 | 202 | lastTime = lastStart = millis(); 203 | startFlag = true; 204 | startDelay = start_Delay; 205 | initFlag = false; 206 | initDisplay = initDisp; 207 | } 208 | 209 | static void sa_stop() 210 | { 211 | i2s_stop(I2S_PORT); 212 | } 213 | 214 | // Externally called activate/deactivate 215 | 216 | void sa_activate(bool init, unsigned long start_Delay) 217 | { 218 | sa_resume(init, start_Delay); 219 | 220 | if(sa_avail) 221 | saActive = true; 222 | } 223 | 224 | void sa_deactivate() 225 | { 226 | if(sa_avail) 227 | sa_stop(); 228 | 229 | saActive = false; 230 | 231 | #if defined(SID_DBG) && defined(SA_DBG_WRITEOUT) 232 | outFile.close(); 233 | outFileOpen = false; 234 | #endif 235 | } 236 | 237 | // Set amplification factor 238 | 239 | int sa_setAmpFact(int newAmpFact) 240 | { 241 | int old = ampFact; 242 | 243 | if(newAmpFact >= 0) { 244 | ampFact = newAmpFact; 245 | } 246 | 247 | return old; 248 | } 249 | 250 | // The loop 251 | 252 | void sa_loop() 253 | { 254 | size_t bytesRead = 0; 255 | unsigned long now = millis(); 256 | int mmaxi = 0, band = 0; 257 | FTYPE mmax = 1.0; 258 | 259 | if(!saActive || !sa_avail) 260 | return; 261 | 262 | if(lastTime && (now - lastTime < (NUMSAMPLES * 1000 / SAMPLERATE))) 263 | return; 264 | 265 | lastTime = now; 266 | 267 | // Read i2c data - do I need a timeout? FIXME 268 | i2s_read(I2S_PORT, (void *)rawSamples, sizeof(rawSamples), &bytesRead, portMAX_DELAY); 269 | 270 | if(bytesRead != sizeof(rawSamples)) { 271 | // what now? 272 | #ifdef SID_DBG 273 | Serial.println("bytesRead != sizeof(rawSamples)"); 274 | #endif 275 | } 276 | 277 | #if defined(SID_DBG) && defined(SA_DBG_WRITEOUT) 278 | 279 | if(outFileOpen) { 280 | outFile.write((uint8_t *)&rawSamples[0], NUMSAMPLES * 4); 281 | } 282 | 283 | #else 284 | 285 | // Convert; clear vImag 286 | for(int i = 0; i < NUMSAMPLES; i++) { 287 | vReal[i] = (FTYPE)(rawSamples[i] / 16384); // do NOT shift; result of shifting negative integer is undefined 288 | vImag[i] = 0.0; 289 | } 290 | 291 | // Do the FFT 292 | arduinoFFT FFT = arduinoFFT(vReal, vImag, NUMSAMPLES, SAMPLERATE); 293 | 294 | // Remove hum and dc offset 295 | FFT.DCRemoval(); 296 | 297 | // Windowing: "Rectangle" does fine for our purpose 298 | // and since this does effectively nothing, skip it. 299 | //FFT.Windowing(FFT_WIN_TYP_RECTANGLE, FFT_FORWARD); 300 | //FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); 301 | 302 | FFT.Compute(FFT_FORWARD); 303 | 304 | //FFT.ComplexToMagnitude(); // Covers entire array, half would do 305 | FFT.ComplexToMagnitude(vReal, vImag, NUMSAMPLES/2); 306 | 307 | // Fill frequency bands 308 | // Max freq = Half of sampling rate => (SAMPLERATE / 2) 309 | // vReal only filled half because of this => (NUMSAMPLES / 2) 310 | band = 0; 311 | for(int i = 3; i < NUMSAMPLES / 2; i++) { 312 | int freq = (i - 2) * (SAMPLERATE / 2) / (NUMSAMPLES / 2); 313 | if(freq >= freqSteps[band]) { 314 | band++; 315 | if(band == NUMBANDS) break; 316 | else freqBands[band] = 0.0; 317 | } 318 | if(band && (vReal[i] > minTreshold[band])) { 319 | freqBands[band] += vReal[i]; 320 | } 321 | } 322 | 323 | // Store absolute band sums to our history table 324 | for(int i = 1; i < NUMBANDS; i++) { 325 | freqBandsHistory[histIdx][i] = freqBands[i]; 326 | } 327 | histIdx++; 328 | histIdx &= (FQ_HIST-1); 329 | 330 | // Find maximum in history table for scaling each bar 331 | for(int i = 1; i < NUMBANDS; i++) { 332 | mmax = 1.0; 333 | for(int j = 0; j < FQ_HIST; j++) { 334 | if(mmax < freqBandsHistory[j][i]) mmax = freqBandsHistory[j][i]; 335 | } 336 | freqBands[i] /= mmax; 337 | } 338 | 339 | now = millis(); 340 | 341 | if(startFlag) { 342 | 343 | if(now - lastStart < startDelay) { 344 | if(!initFlag) { 345 | for(int i = 0; i < DISPLAYBANDS; i++) { 346 | peaks[i] = 0; 347 | newPeak[i] = now; 348 | peakTimer[i] = PEAK_HOLD; 349 | oldHeight[i] = 1; 350 | if(initDisplay) { 351 | sid.drawBarWithHeight(i, 1); 352 | } 353 | } 354 | if(initDisplay) { 355 | sid.show(); 356 | } 357 | initFlag = true; 358 | } 359 | } else { 360 | startFlag = false; 361 | histIdx = 0; 362 | for(int i = 0; i < FQ_HIST; i++) { 363 | for(int j = 1; j < NUMBANDS; j++) { 364 | freqBandsHistory[i][j] = 0.0; 365 | } 366 | } 367 | } 368 | 369 | } else { 370 | 371 | // Calculate bar heights 372 | for(int i = 0; i < DISPLAYBANDS; i++) { 373 | int height = (int)(freqBands[i+1] * (FTYPE)(LEDS_PER_BAR - 1)); 374 | 375 | if(ampFact != 100) { 376 | if(!height) height = 1; 377 | height = height * ampFact / 100; 378 | if(height > maxTTHeight[i]) height = maxTTHeight[i]; 379 | } 380 | 381 | if(height > LEDS_PER_BAR) height = LEDS_PER_BAR; 382 | if(!height) height = 1; 383 | 384 | // Smoothen jumps in downward direction 385 | if(height < oldHeight[i]) { 386 | if(oldHeight[i] - height > 10) height = (oldHeight[i] + height) / 2; 387 | else height = oldHeight[i] - 1; 388 | } 389 | 390 | // Now do peak 391 | if(height - 1 > peaks[i]) { 392 | peaks[i] = min(LEDS_PER_BAR - 1, height - 1); 393 | newPeak[i] = now; 394 | peakTimer[i] = PEAK_HOLD; 395 | } 396 | 397 | oldHeight[i] = height; 398 | 399 | // Draw bars & peaks 400 | sid.drawBarWithHeight(i, oldHeight[i]); 401 | if(doPeaks && peaks[i] > oldHeight[i] - 1) { 402 | sid.drawDot(i, peaks[i]); 403 | } 404 | } 405 | 406 | // Put result on display 407 | sid.show(); 408 | } 409 | 410 | now = millis(); 411 | 412 | // Make peaks fall down 413 | for(int i = 0; i < DISPLAYBANDS; i++) { 414 | if(newPeak[i] && now - newPeak[i] > peakTimer[i]) { 415 | if(peaks[i] > 0) { 416 | peakTimer[i] = PEAK_FALL; 417 | newPeak[i] = now; 418 | peaks[i]--; 419 | } else { 420 | newPeak[i] = 0; 421 | } 422 | } 423 | } 424 | 425 | #endif 426 | } 427 | -------------------------------------------------------------------------------- /src/sid_siddly.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Siddly 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #include "sid_global.h" 53 | 54 | #include 55 | 56 | #include "sid_siddly.h" 57 | #include "sid_main.h" 58 | 59 | #define WIDTH 10 60 | #define HEIGHT 19 61 | 62 | #define NUM_LEVELS 9 63 | #define PIECES_PER_LEVEL 40 64 | 65 | bool siActive = false; 66 | 67 | static uint8_t board[HEIGHT][WIDTH] = { { 0 } }; 68 | 69 | #define NUM_PIECES 7 70 | static uint8_t p1[3][3] = { {0,0,1}, {1,1,1}, {0,0,0} }; 71 | static uint8_t p2[3][3] = { {1,0,0}, {1,1,1}, {0,0,0} }; 72 | static uint8_t p3[3][3] = { {0,1,1}, {1,1,0}, {0,0,0} }; 73 | static uint8_t p4[3][3] = { {1,1,0}, {0,1,1}, {0,0,0} }; 74 | static uint8_t p5[3][3] = { {0,1,0}, {1,1,1}, {0,0,0} }; 75 | static uint8_t p6[2][2] = { {1,1}, {1,1} }; 76 | static uint8_t p7[4][4] = { {0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0} }; 77 | 78 | static uint8_t *pd[NUM_PIECES] = { 79 | (uint8_t *)p1, (uint8_t *)p2, (uint8_t *)p3, (uint8_t *)p4, (uint8_t *)p5, (uint8_t *)p6, (uint8_t *)p7 80 | }; 81 | 82 | static uint8_t ps[NUM_PIECES] = { 3, 3, 3, 3, 3, 2, 4 }; 83 | 84 | static unsigned long ldelays[NUM_LEVELS] = { 85 | 1000, 900, 800, 700, 600, 500, 400, 300, 200 86 | }; 87 | 88 | static uint8_t cp = 0; // current piece index 89 | static uint8_t cps = 0; // current piece size 90 | static uint8_t cpd[4][4] = { { 0 } }; // current piece data (copy of pX above) 91 | static int cpx = 0; // current x position 92 | static int cpy = 0; // current y position 93 | 94 | static unsigned long cp_now = 0; 95 | 96 | static unsigned long siStartup = 0; // startup sequence running 97 | static bool havePiece = false; 98 | static bool gameOver = false; 99 | static bool gameOverShown = false; 100 | static bool removeCycle = false; 101 | static bool pauseGame = false; 102 | static bool pauseShown = false; 103 | static int level = 0; // current level (speed) 104 | static int pcnt = 0; // piece count in level 105 | 106 | static void clearBoard() 107 | { 108 | memset(board, 0, sizeof(board)); 109 | } 110 | 111 | static void setBoardAt(int x, int y) 112 | { 113 | if(y < 0 || y > HEIGHT - 1) return; 114 | if(x < 0 || x > WIDTH - 1) return; 115 | 116 | board[y][x] = 1; 117 | } 118 | 119 | static int boardAt(int x, int y) 120 | { 121 | if(y < 0 || y > HEIGHT - 1) return 0; 122 | if(x < 0 || x > WIDTH - 1) return 0; 123 | 124 | return board[y][x]; 125 | } 126 | 127 | static bool lineIsFull(int y) 128 | { 129 | for(int x = 0; x < WIDTH; x++) { 130 | if(!board[y][x]) 131 | return false; 132 | } 133 | return true; 134 | } 135 | 136 | static void copyLine(int sy, int dy) 137 | { 138 | for(int x = 0; x < WIDTH; x++) { 139 | board[dy][x] = board[sy][x]; 140 | } 141 | } 142 | 143 | static void clearLine(int y) 144 | { 145 | for(int x = 0; x < WIDTH; x++) { 146 | board[y][x] = 0; 147 | } 148 | } 149 | 150 | static void removeLine(int yy) 151 | { 152 | if(yy > 0) { 153 | for(int y = yy; y > 0; y--) { 154 | copyLine(y - 1, y); 155 | } 156 | } 157 | 158 | clearLine(0); 159 | } 160 | 161 | static void removeFullLines() 162 | { 163 | for(int y = HEIGHT - 1; y >= 0; y--) { 164 | if(lineIsFull(y)) { 165 | removeLine(y); 166 | y++; 167 | } 168 | } 169 | } 170 | 171 | static bool canPlace() 172 | { 173 | bool mat = false; 174 | int l = 0; 175 | 176 | for(int y = cps - 1; y >= 0; y--) { 177 | for(int x = 0; x < cps; x++) { 178 | if(cpd[y][x]) { 179 | mat = true; 180 | if(boardAt(cpx + x, cpy)) 181 | return false; 182 | } 183 | } 184 | if(mat) l++; 185 | if(cpy - l < 0) 186 | break; 187 | } 188 | return true; 189 | } 190 | 191 | static bool canRotate() 192 | { 193 | for(int y = 0; y < cps; y++) { 194 | for(int x = 0; x < cps; x++) { 195 | if(cpd[y][x]) { 196 | int ny = cpy + cps - 1 - x; 197 | int nx = cpx + y; 198 | if((ny >= HEIGHT) || 199 | (nx < 0 || nx >= WIDTH) || 200 | (boardAt(nx, ny))) 201 | return false; 202 | } 203 | } 204 | } 205 | return true; 206 | } 207 | 208 | static void rotate() 209 | { 210 | uint8_t cpdb[4][4] = { { 0 } }; 211 | 212 | for(int y = 0; y < cps; y++) { 213 | for(int x = 0; x < cps; x++) { 214 | cpdb[cps - 1 - x][y] = cpd[y][x]; 215 | } 216 | } 217 | memcpy(cpd, cpdb, sizeof(cpd)); 218 | } 219 | 220 | static bool canMoveDown() 221 | { 222 | for(int y = 0; y < cps; y++) { 223 | for(int x = 0; x < cps; x++) { 224 | if(cpd[y][x]) { 225 | if((cpy + y + 1 >= HEIGHT) || 226 | boardAt(cpx + x, cpy + y + 1)) 227 | return false; 228 | } 229 | } 230 | } 231 | return true; 232 | } 233 | 234 | static void moveDown() 235 | { 236 | cpy++; 237 | } 238 | 239 | static bool canMoveLeft() 240 | { 241 | for(int y = 0; y < cps; y++) { 242 | for(int x = 0; x < cps; x++) { 243 | if(cpd[y][x]) { 244 | if((cpx + x - 1 < 0) || 245 | boardAt(cpx + x - 1, cpy + y)) 246 | return false; 247 | } 248 | } 249 | } 250 | return true; 251 | } 252 | 253 | static void moveLeft() 254 | { 255 | cpx--; 256 | } 257 | 258 | static bool canMoveRight() 259 | { 260 | for(int y = 0; y < cps; y++) { 261 | for(int x = 0; x < cps; x++) { 262 | if(cpd[y][x]) { 263 | if((cpx + x + 1 >= WIDTH) || 264 | boardAt(cpx + x + 1, cpy + y)) 265 | return false; 266 | } 267 | } 268 | } 269 | return true; 270 | } 271 | 272 | static void moveRight() 273 | { 274 | cpx++; 275 | } 276 | 277 | static bool newPiece() 278 | { 279 | cp = esp_random() % NUM_PIECES; 280 | cps = ps[cp]; 281 | cpx = (WIDTH - cps) / 2; 282 | cpy = 0; 283 | 284 | uint8_t *npd = pd[cp]; 285 | for(int y = 0; y < cps; y++) { 286 | for(int x = 0; x < cps; x++) { 287 | cpd[y][x] = *(npd + (y * cps) + x); 288 | } 289 | } 290 | 291 | if(canPlace()) { 292 | cp_now = millis(); 293 | pcnt++; 294 | havePiece = true; 295 | } else { 296 | gameOver = true; 297 | havePiece = false; 298 | return false; 299 | } 300 | 301 | return true; 302 | } 303 | 304 | static void updateDisplay() 305 | { 306 | uint8_t myField[WIDTH * (HEIGHT + 1)]; 307 | 308 | memset((void *)myField, 0, WIDTH); 309 | 310 | for(int i = 0; i < min(10, ((PIECES_PER_LEVEL - pcnt) * 10 / PIECES_PER_LEVEL) + 1); i++) { 311 | myField[i] = 1; 312 | } 313 | 314 | memcpy((void *)(myField + WIDTH), (void *)board, WIDTH * HEIGHT); 315 | 316 | if(havePiece) { 317 | for(int y = 0; y < cps; y++) { 318 | for(int x = 0; x < cps; x++) { 319 | if(cpd[y][x]) { 320 | myField[((cpy + y + 1) * WIDTH) + cpx + x] = 1; 321 | } 322 | } 323 | } 324 | } 325 | sid.drawFieldAndShow((uint8_t *)myField); 326 | } 327 | 328 | static void resetGame() 329 | { 330 | pcnt = 0; 331 | level = 0; 332 | gameOver = gameOverShown = false; 333 | pauseGame = pauseShown = false; 334 | 335 | clearBoard(); 336 | } 337 | 338 | void si_init() // start game 339 | { 340 | resetGame(); 341 | 342 | showWordSequence("SIDDLY", 2); 343 | 344 | siStartup = millis(); 345 | siActive = true; 346 | } 347 | 348 | void si_loop() 349 | { 350 | unsigned long now = millis(); 351 | 352 | if(!siActive) 353 | return; 354 | 355 | if(gameOver) { 356 | if(!gameOverShown) { 357 | showWordSequence("GAME OVER ", 1); 358 | gameOverShown = true; 359 | return; 360 | } 361 | resetGame(); 362 | newPiece(); 363 | updateDisplay(); 364 | return; 365 | } 366 | 367 | if(siStartup) { 368 | if(now - siStartup < 1000) { 369 | return; 370 | } 371 | siStartup = 0; 372 | newPiece(); 373 | updateDisplay(); 374 | return; 375 | } 376 | 377 | if(pauseGame) { 378 | if(!pauseShown) { 379 | sid.drawLetterAndShow('P'); 380 | pauseShown = true; 381 | } 382 | return; 383 | } 384 | 385 | if(now - cp_now < ldelays[level]) 386 | return; 387 | 388 | if(removeCycle) { 389 | // Remove filled lines 390 | removeFullLines(); 391 | // Advance level if piece count is reached 392 | if(pcnt >= PIECES_PER_LEVEL) { 393 | pcnt = 0; 394 | if(level < NUM_LEVELS - 1) { 395 | char lvl[2]; 396 | clearBoard(); 397 | level++; 398 | lvl[0] = level + 1 + '0'; 399 | lvl[1] = 0; 400 | showWordSequence(lvl, 3); 401 | } else { 402 | // User won all levels; restart for now 403 | clearBoard(); 404 | level = 0; 405 | } 406 | } 407 | removeCycle = false; 408 | // Create new piece 409 | newPiece(); 410 | } else if(canMoveDown()) { 411 | moveDown(); 412 | cp_now = now; 413 | } else { 414 | // Put piece into board 415 | for(int y = 0; y < cps; y++) { 416 | for(int x = 0; x < cps; x++) { 417 | if(cpd[y][x]) { 418 | setBoardAt(cpx + x, cpy + y); 419 | } 420 | } 421 | } 422 | removeCycle = true; 423 | cp_now = now; 424 | } 425 | 426 | updateDisplay(); 427 | } 428 | 429 | void si_end() 430 | { 431 | if(!siActive) 432 | return; 433 | 434 | siActive = false; 435 | } 436 | 437 | void si_newGame() 438 | { 439 | if(!siActive || siStartup) 440 | return; 441 | 442 | resetGame(); 443 | 444 | newPiece(); 445 | updateDisplay(); 446 | } 447 | 448 | void si_pause() 449 | { 450 | if(!siActive || gameOver || siStartup) 451 | return; 452 | 453 | pauseGame = !pauseGame; 454 | pauseShown = false; 455 | } 456 | 457 | void si_moveRight() // move right 458 | { 459 | if(!siActive || gameOver || siStartup || !havePiece || pauseGame) 460 | return; 461 | 462 | if(canMoveRight()) { 463 | moveRight(); 464 | updateDisplay(); 465 | } 466 | } 467 | 468 | void si_moveLeft() // move left 469 | { 470 | if(!siActive || gameOver || siStartup || !havePiece || pauseGame) 471 | return; 472 | 473 | if(canMoveLeft()) { 474 | moveLeft(); 475 | updateDisplay(); 476 | } 477 | } 478 | 479 | void si_moveDown() // move down 480 | { 481 | if(!siActive || gameOver || siStartup || !havePiece || pauseGame) 482 | return; 483 | 484 | if(canMoveDown()) { 485 | moveDown(); 486 | updateDisplay(); 487 | } 488 | } 489 | 490 | 491 | void si_fallDown() // fall down 492 | { 493 | 494 | if(!siActive || gameOver || siStartup || !havePiece || pauseGame) 495 | return; 496 | 497 | while(canMoveDown()) { 498 | moveDown(); 499 | } 500 | updateDisplay(); 501 | } 502 | 503 | void si_rotate() // rotate (left) 504 | { 505 | if(!siActive || gameOver || siStartup || !havePiece || pauseGame) 506 | return; 507 | 508 | if(canRotate()) { 509 | rotate(); 510 | updateDisplay(); 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /src/src/arduinoFFT/arduinoFFT.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | FFT library 4 | Copyright (C) 2010 Didier Longueville 5 | Copyright (C) 2014 Enrique Condes 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | #include "arduinoFFT.h" 23 | 24 | arduinoFFT::arduinoFFT(void) { // Constructor 25 | //#warning("This method is deprecated and may be removed on future revisions.") 26 | } 27 | 28 | arduinoFFT::arduinoFFT(FTYPE *vReal, FTYPE *vImag, uint16_t samples, 29 | FTYPE samplingFrequency) { // Constructor 30 | this->_vReal = vReal; 31 | this->_vImag = vImag; 32 | this->_samples = samples; 33 | this->_samplingFrequency = samplingFrequency; 34 | this->_power = Exponent(samples); 35 | } 36 | 37 | arduinoFFT::~arduinoFFT(void) { 38 | // Destructor 39 | } 40 | 41 | uint8_t arduinoFFT::Revision(void) { return (FFT_LIB_REV); } 42 | 43 | void arduinoFFT::Compute(FTYPE *vReal, FTYPE *vImag, uint16_t samples, 44 | FFTDirection dir) { 45 | //#warning("This method is deprecated and may be removed on future revisions.") 46 | Compute(vReal, vImag, samples, Exponent(samples), dir); 47 | } 48 | 49 | void arduinoFFT::Compute(FFTDirection dir) { 50 | // Computes in-place complex-to-complex FFT / 51 | // Reverse bits / 52 | uint16_t j = 0; 53 | for (uint16_t i = 0; i < (this->_samples - 1); i++) { 54 | if (i < j) { 55 | Swap(&this->_vReal[i], &this->_vReal[j]); 56 | if (dir == FFT_REVERSE) 57 | Swap(&this->_vImag[i], &this->_vImag[j]); 58 | } 59 | uint16_t k = (this->_samples >> 1); 60 | while (k <= j) { 61 | j -= k; 62 | k >>= 1; 63 | } 64 | j += k; 65 | } 66 | // Compute the FFT 67 | #ifdef __AVR__ 68 | uint8_t index = 0; 69 | #endif 70 | FTYPE c1 = -1.0; 71 | FTYPE c2 = 0.0; 72 | uint16_t l2 = 1; 73 | for (uint8_t l = 0; (l < this->_power); l++) { 74 | uint16_t l1 = l2; 75 | l2 <<= 1; 76 | FTYPE u1 = 1.0; 77 | FTYPE u2 = 0.0; 78 | for (j = 0; j < l1; j++) { 79 | for (uint16_t i = j; i < this->_samples; i += l2) { 80 | uint16_t i1 = i + l1; 81 | FTYPE t1 = u1 * this->_vReal[i1] - u2 * this->_vImag[i1]; 82 | FTYPE t2 = u1 * this->_vImag[i1] + u2 * this->_vReal[i1]; 83 | this->_vReal[i1] = this->_vReal[i] - t1; 84 | this->_vImag[i1] = this->_vImag[i] - t2; 85 | this->_vReal[i] += t1; 86 | this->_vImag[i] += t2; 87 | } 88 | FTYPE z = ((u1 * c1) - (u2 * c2)); 89 | u2 = ((u1 * c2) + (u2 * c1)); 90 | u1 = z; 91 | } 92 | #ifdef __AVR__ 93 | c2 = pgm_read_float_near(&(_c2[index])); 94 | c1 = pgm_read_float_near(&(_c1[index])); 95 | index++; 96 | #else 97 | c2 = FFT_SQRT((1.0 - c1) / 2.0); 98 | c1 = FFT_SQRT((1.0 + c1) / 2.0); 99 | #endif 100 | if (dir == FFT_FORWARD) { 101 | c2 = -c2; 102 | } 103 | } 104 | // Scaling for reverse transform / 105 | if (dir != FFT_FORWARD) { 106 | for (uint16_t i = 0; i < this->_samples; i++) { 107 | this->_vReal[i] /= this->_samples; 108 | this->_vImag[i] /= this->_samples; 109 | } 110 | } 111 | } 112 | 113 | void arduinoFFT::Compute(FTYPE *vReal, FTYPE *vImag, uint16_t samples, 114 | uint8_t power, FFTDirection dir) { 115 | // Computes in-place complex-to-complex FFT 116 | // Reverse bits 117 | //#warning("This method is deprecated and may be removed on future revisions.") 118 | uint16_t j = 0; 119 | for (uint16_t i = 0; i < (samples - 1); i++) { 120 | if (i < j) { 121 | Swap(&vReal[i], &vReal[j]); 122 | if (dir == FFT_REVERSE) 123 | Swap(&vImag[i], &vImag[j]); 124 | } 125 | uint16_t k = (samples >> 1); 126 | while (k <= j) { 127 | j -= k; 128 | k >>= 1; 129 | } 130 | j += k; 131 | } 132 | // Compute the FFT 133 | #ifdef __AVR__ 134 | uint8_t index = 0; 135 | #endif 136 | FTYPE c1 = -1.0; 137 | FTYPE c2 = 0.0; 138 | uint16_t l2 = 1; 139 | for (uint8_t l = 0; (l < power); l++) { 140 | uint16_t l1 = l2; 141 | l2 <<= 1; 142 | FTYPE u1 = 1.0; 143 | FTYPE u2 = 0.0; 144 | for (j = 0; j < l1; j++) { 145 | for (uint16_t i = j; i < samples; i += l2) { 146 | uint16_t i1 = i + l1; 147 | FTYPE t1 = u1 * vReal[i1] - u2 * vImag[i1]; 148 | FTYPE t2 = u1 * vImag[i1] + u2 * vReal[i1]; 149 | vReal[i1] = vReal[i] - t1; 150 | vImag[i1] = vImag[i] - t2; 151 | vReal[i] += t1; 152 | vImag[i] += t2; 153 | } 154 | FTYPE z = ((u1 * c1) - (u2 * c2)); 155 | u2 = ((u1 * c2) + (u2 * c1)); 156 | u1 = z; 157 | } 158 | #ifdef __AVR__ 159 | c2 = pgm_read_float_near(&(_c2[index])); 160 | c1 = pgm_read_float_near(&(_c1[index])); 161 | index++; 162 | #else 163 | c2 = FFT_SQRT((1.0 - c1) / 2.0); 164 | c1 = FFT_SQRT((1.0 + c1) / 2.0); 165 | #endif 166 | if (dir == FFT_FORWARD) { 167 | c2 = -c2; 168 | } 169 | } 170 | // Scaling for reverse transform 171 | if (dir != FFT_FORWARD) { 172 | for (uint16_t i = 0; i < samples; i++) { 173 | vReal[i] /= samples; 174 | vImag[i] /= samples; 175 | } 176 | } 177 | } 178 | 179 | void arduinoFFT::ComplexToMagnitude() { 180 | // vM is half the size of vReal and vImag 181 | for (uint16_t i = 0; i < this->_samples; i++) { 182 | this->_vReal[i] = FFT_SQRT(sq(this->_vReal[i]) + sq(this->_vImag[i])); 183 | } 184 | } 185 | 186 | void arduinoFFT::ComplexToMagnitude(FTYPE *vReal, FTYPE *vImag, 187 | uint16_t samples) { 188 | // vM is half the size of vReal and vImag 189 | //#warning("This method is deprecated and may be removed on future revisions.") 190 | for (uint16_t i = 0; i < samples; i++) { 191 | vReal[i] = FFT_SQRT(sq(vReal[i]) + sq(vImag[i])); 192 | } 193 | } 194 | 195 | void arduinoFFT::DCRemoval() { 196 | // calculate the mean of vData 197 | FTYPE mean = 0; 198 | for (uint16_t i = 0; i < this->_samples; i++) { 199 | mean += this->_vReal[i]; 200 | } 201 | mean /= this->_samples; 202 | // Subtract the mean from vData 203 | for (uint16_t i = 0; i < this->_samples; i++) { 204 | this->_vReal[i] -= mean; 205 | } 206 | } 207 | 208 | void arduinoFFT::DCRemoval(FTYPE *vData, uint16_t samples) { 209 | // calculate the mean of vData 210 | //#warning("This method is deprecated and may be removed on future revisions.") 211 | FTYPE mean = 0; 212 | for (uint16_t i = 0; i < samples; i++) { 213 | mean += vData[i]; 214 | } 215 | mean /= samples; 216 | // Subtract the mean from vData 217 | for (uint16_t i = 0; i < samples; i++) { 218 | vData[i] -= mean; 219 | } 220 | } 221 | 222 | void arduinoFFT::Windowing(FFTWindow windowType, FFTDirection dir) { 223 | // Weighing factors are computed once before multiple use of FFT 224 | // The weighing function is symmetric; half the weighs are recorded 225 | FTYPE samplesMinusOne = (FTYPE(this->_samples) - 1.0); 226 | for (uint16_t i = 0; i < (this->_samples >> 1); i++) { 227 | FTYPE indexMinusOne = FTYPE(i); 228 | FTYPE ratio = (indexMinusOne / samplesMinusOne); 229 | FTYPE weighingFactor = 1.0; 230 | // Compute and record weighting factor 231 | switch (windowType) { 232 | case FFT_WIN_TYP_RECTANGLE: // rectangle (box car) 233 | weighingFactor = 1.0; 234 | break; 235 | case FFT_WIN_TYP_HAMMING: // hamming 236 | weighingFactor = 0.54 - (0.46 * FFT_COS(twoPi * ratio)); 237 | break; 238 | case FFT_WIN_TYP_HANN: // hann 239 | weighingFactor = 0.54 * (1.0 - FFT_COS(twoPi * ratio)); 240 | break; 241 | case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett) 242 | #if defined(ESP8266) || defined(ESP32) 243 | weighingFactor = 244 | 1.0 - ((2.0 * fabs(indexMinusOne - (samplesMinusOne / 2.0))) / 245 | samplesMinusOne); 246 | #else 247 | weighingFactor = 248 | 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / 249 | samplesMinusOne); 250 | #endif 251 | break; 252 | case FFT_WIN_TYP_NUTTALL: // nuttall 253 | weighingFactor = 0.355768 - (0.487396 * (FFT_COS(twoPi * ratio))) + 254 | (0.144232 * (FFT_COS(fourPi * ratio))) - 255 | (0.012604 * (FFT_COS(sixPi * ratio))); 256 | break; 257 | case FFT_WIN_TYP_BLACKMAN: // blackman 258 | weighingFactor = 0.42323 - (0.49755 * (FFT_COS(twoPi * ratio))) + 259 | (0.07922 * (FFT_COS(fourPi * ratio))); 260 | break; 261 | case FFT_WIN_TYP_BLACKMAN_NUTTALL: // blackman nuttall 262 | weighingFactor = 0.3635819 - (0.4891775 * (FFT_COS(twoPi * ratio))) + 263 | (0.1365995 * (FFT_COS(fourPi * ratio))) - 264 | (0.0106411 * (FFT_COS(sixPi * ratio))); 265 | break; 266 | case FFT_WIN_TYP_BLACKMAN_HARRIS: // blackman harris 267 | weighingFactor = 0.35875 - (0.48829 * (FFT_COS(twoPi * ratio))) + 268 | (0.14128 * (FFT_COS(fourPi * ratio))) - 269 | (0.01168 * (FFT_COS(sixPi * ratio))); 270 | break; 271 | case FFT_WIN_TYP_FLT_TOP: // flat top 272 | weighingFactor = 0.2810639 - (0.5208972 * FFT_COS(twoPi * ratio)) + 273 | (0.1980399 * FFT_COS(fourPi * ratio)); 274 | break; 275 | case FFT_WIN_TYP_WELCH: // welch 276 | weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / 277 | (samplesMinusOne / 2.0)); 278 | break; 279 | } 280 | if (dir == FFT_FORWARD) { 281 | this->_vReal[i] *= weighingFactor; 282 | this->_vReal[this->_samples - (i + 1)] *= weighingFactor; 283 | } else { 284 | this->_vReal[i] /= weighingFactor; 285 | this->_vReal[this->_samples - (i + 1)] /= weighingFactor; 286 | } 287 | } 288 | } 289 | 290 | void arduinoFFT::Windowing(FTYPE *vData, uint16_t samples, 291 | FFTWindow windowType, FFTDirection dir) { 292 | // Weighing factors are computed once before multiple use of FFT 293 | // The weighing function is symetric; half the weighs are recorded 294 | //#warning("This method is deprecated and may be removed on future revisions.") 295 | FTYPE samplesMinusOne = (FTYPE(samples) - 1.0); 296 | for (uint16_t i = 0; i < (samples >> 1); i++) { 297 | FTYPE indexMinusOne = FTYPE(i); 298 | FTYPE ratio = (indexMinusOne / samplesMinusOne); 299 | FTYPE weighingFactor = 1.0; 300 | // Compute and record weighting factor 301 | switch (windowType) { 302 | case FFT_WIN_TYP_RECTANGLE: // rectangle (box car) 303 | weighingFactor = 1.0; 304 | break; 305 | case FFT_WIN_TYP_HAMMING: // hamming 306 | weighingFactor = 0.54 - (0.46 * FFT_COS(twoPi * ratio)); 307 | break; 308 | case FFT_WIN_TYP_HANN: // hann 309 | weighingFactor = 0.54 * (1.0 - FFT_COS(twoPi * ratio)); 310 | break; 311 | case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett) 312 | #if defined(ESP8266) || defined(ESP32) 313 | weighingFactor = 314 | 1.0 - ((2.0 * fabs(indexMinusOne - (samplesMinusOne / 2.0))) / 315 | samplesMinusOne); 316 | #else 317 | weighingFactor = 318 | 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / 319 | samplesMinusOne); 320 | #endif 321 | break; 322 | case FFT_WIN_TYP_NUTTALL: // nuttall 323 | weighingFactor = 0.355768 - (0.487396 * (FFT_COS(twoPi * ratio))) + 324 | (0.144232 * (FFT_COS(fourPi * ratio))) - 325 | (0.012604 * (FFT_COS(sixPi * ratio))); 326 | break; 327 | case FFT_WIN_TYP_BLACKMAN: // blackman 328 | weighingFactor = 0.42323 - (0.49755 * (FFT_COS(twoPi * ratio))) + 329 | (0.07922 * (FFT_COS(fourPi * ratio))); 330 | break; 331 | case FFT_WIN_TYP_BLACKMAN_NUTTALL: // blackman nuttall 332 | weighingFactor = 0.3635819 - (0.4891775 * (FFT_COS(twoPi * ratio))) + 333 | (0.1365995 * (FFT_COS(fourPi * ratio))) - 334 | (0.0106411 * (FFT_COS(sixPi * ratio))); 335 | break; 336 | case FFT_WIN_TYP_BLACKMAN_HARRIS: // blackman harris 337 | weighingFactor = 0.35875 - (0.48829 * (FFT_COS(twoPi * ratio))) + 338 | (0.14128 * (FFT_COS(fourPi * ratio))) - 339 | (0.01168 * (FFT_COS(sixPi * ratio))); 340 | break; 341 | case FFT_WIN_TYP_FLT_TOP: // flat top 342 | weighingFactor = 0.2810639 - (0.5208972 * FFT_COS(twoPi * ratio)) + 343 | (0.1980399 * FFT_COS(fourPi * ratio)); 344 | break; 345 | case FFT_WIN_TYP_WELCH: // welch 346 | weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / 347 | (samplesMinusOne / 2.0)); 348 | break; 349 | } 350 | if (dir == FFT_FORWARD) { 351 | vData[i] *= weighingFactor; 352 | vData[samples - (i + 1)] *= weighingFactor; 353 | } else { 354 | vData[i] /= weighingFactor; 355 | vData[samples - (i + 1)] /= weighingFactor; 356 | } 357 | } 358 | } 359 | 360 | FTYPE arduinoFFT::MajorPeak() { 361 | FTYPE maxY = 0; 362 | uint16_t IndexOfMaxY = 0; 363 | // If sampling_frequency = 2 * max_frequency in signal, 364 | // value would be stored at position samples/2 365 | for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) { 366 | if ((this->_vReal[i - 1] < this->_vReal[i]) && 367 | (this->_vReal[i] > this->_vReal[i + 1])) { 368 | if (this->_vReal[i] > maxY) { 369 | maxY = this->_vReal[i]; 370 | IndexOfMaxY = i; 371 | } 372 | } 373 | } 374 | FTYPE delta = 375 | 0.5 * 376 | ((this->_vReal[IndexOfMaxY - 1] - this->_vReal[IndexOfMaxY + 1]) / 377 | (this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + 378 | this->_vReal[IndexOfMaxY + 1])); 379 | FTYPE interpolatedX = 380 | ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples - 1); 381 | if (IndexOfMaxY == 382 | (this->_samples >> 1)) // To improve calculation on edge values 383 | interpolatedX = 384 | ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples); 385 | // returned value: interpolated frequency peak apex 386 | return (interpolatedX); 387 | } 388 | 389 | void arduinoFFT::MajorPeak(FTYPE *f, FTYPE *v) { 390 | FTYPE maxY = 0; 391 | uint16_t IndexOfMaxY = 0; 392 | // If sampling_frequency = 2 * max_frequency in signal, 393 | // value would be stored at position samples/2 394 | for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) { 395 | if ((this->_vReal[i - 1] < this->_vReal[i]) && 396 | (this->_vReal[i] > this->_vReal[i + 1])) { 397 | if (this->_vReal[i] > maxY) { 398 | maxY = this->_vReal[i]; 399 | IndexOfMaxY = i; 400 | } 401 | } 402 | } 403 | FTYPE delta = 404 | 0.5 * 405 | ((this->_vReal[IndexOfMaxY - 1] - this->_vReal[IndexOfMaxY + 1]) / 406 | (this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + 407 | this->_vReal[IndexOfMaxY + 1])); 408 | FTYPE interpolatedX = 409 | ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples - 1); 410 | if (IndexOfMaxY == 411 | (this->_samples >> 1)) // To improve calculation on edge values 412 | interpolatedX = 413 | ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples); 414 | // returned value: interpolated frequency peak apex 415 | *f = interpolatedX; 416 | #if defined(ESP8266) || defined(ESP32) 417 | *v = fabs(this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + 418 | this->_vReal[IndexOfMaxY + 1]); 419 | #else 420 | *v = abs(this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + 421 | this->_vReal[IndexOfMaxY + 1]); 422 | #endif 423 | } 424 | 425 | FTYPE arduinoFFT::MajorPeak(FTYPE *vD, uint16_t samples, 426 | FTYPE samplingFrequency) { 427 | //#warning("This method is deprecated and may be removed on future revisions.") 428 | FTYPE maxY = 0; 429 | uint16_t IndexOfMaxY = 0; 430 | // If sampling_frequency = 2 * max_frequency in signal, 431 | // value would be stored at position samples/2 432 | for (uint16_t i = 1; i < ((samples >> 1) + 1); i++) { 433 | if ((vD[i - 1] < vD[i]) && (vD[i] > vD[i + 1])) { 434 | if (vD[i] > maxY) { 435 | maxY = vD[i]; 436 | IndexOfMaxY = i; 437 | } 438 | } 439 | } 440 | FTYPE delta = 441 | 0.5 * 442 | ((vD[IndexOfMaxY - 1] - vD[IndexOfMaxY + 1]) / 443 | (vD[IndexOfMaxY - 1] - (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY + 1])); 444 | FTYPE interpolatedX = 445 | ((IndexOfMaxY + delta) * samplingFrequency) / (samples - 1); 446 | if (IndexOfMaxY == (samples >> 1)) // To improve calculation on edge values 447 | interpolatedX = ((IndexOfMaxY + delta) * samplingFrequency) / (samples); 448 | // returned value: interpolated frequency peak apex 449 | return (interpolatedX); 450 | } 451 | 452 | void arduinoFFT::MajorPeak(FTYPE *vD, uint16_t samples, 453 | FTYPE samplingFrequency, FTYPE *f, FTYPE *v) { 454 | //#warning("This method is deprecated and may be removed on future revisions.") 455 | FTYPE maxY = 0; 456 | uint16_t IndexOfMaxY = 0; 457 | // If sampling_frequency = 2 * max_frequency in signal, 458 | // value would be stored at position samples/2 459 | for (uint16_t i = 1; i < ((samples >> 1) + 1); i++) { 460 | if ((vD[i - 1] < vD[i]) && (vD[i] > vD[i + 1])) { 461 | if (vD[i] > maxY) { 462 | maxY = vD[i]; 463 | IndexOfMaxY = i; 464 | } 465 | } 466 | } 467 | FTYPE delta = 468 | 0.5 * 469 | ((vD[IndexOfMaxY - 1] - vD[IndexOfMaxY + 1]) / 470 | (vD[IndexOfMaxY - 1] - (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY + 1])); 471 | FTYPE interpolatedX = 472 | ((IndexOfMaxY + delta) * samplingFrequency) / (samples - 1); 473 | // FTYPE popo = 474 | if (IndexOfMaxY == (samples >> 1)) // To improve calculation on edge values 475 | interpolatedX = ((IndexOfMaxY + delta) * samplingFrequency) / (samples); 476 | // returned value: interpolated frequency peak apex 477 | *f = interpolatedX; 478 | #if defined(ESP8266) || defined(ESP32) 479 | *v = 480 | fabs(vD[IndexOfMaxY - 1] - (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY + 1]); 481 | #else 482 | *v = abs(vD[IndexOfMaxY - 1] - (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY + 1]); 483 | #endif 484 | } 485 | 486 | FTYPE arduinoFFT::MajorPeakParabola() { 487 | FTYPE maxY = 0; 488 | uint16_t IndexOfMaxY = 0; 489 | // If sampling_frequency = 2 * max_frequency in signal, 490 | // value would be stored at position samples/2 491 | for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) { 492 | if ((this->_vReal[i - 1] < this->_vReal[i]) && 493 | (this->_vReal[i] > this->_vReal[i + 1])) { 494 | if (this->_vReal[i] > maxY) { 495 | maxY = this->_vReal[i]; 496 | IndexOfMaxY = i; 497 | } 498 | } 499 | } 500 | 501 | FTYPE freq = 0; 502 | if (IndexOfMaxY > 0) { 503 | // Assume the three points to be on a parabola 504 | FTYPE a, b, c; 505 | Parabola(IndexOfMaxY - 1, this->_vReal[IndexOfMaxY - 1], IndexOfMaxY, 506 | this->_vReal[IndexOfMaxY], IndexOfMaxY + 1, 507 | this->_vReal[IndexOfMaxY + 1], &a, &b, &c); 508 | 509 | // Peak is at the middle of the parabola 510 | FTYPE x = -b / (2 * a); 511 | 512 | // And magnitude is at the extrema of the parabola if you want It... 513 | // FTYPE y = a*x*x+b*x+c; 514 | 515 | // Convert to frequency 516 | freq = (x * this->_samplingFrequency) / (this->_samples); 517 | } 518 | 519 | return freq; 520 | } 521 | 522 | void arduinoFFT::Parabola(FTYPE x1, FTYPE y1, FTYPE x2, FTYPE y2, FTYPE x3, 523 | FTYPE y3, FTYPE *a, FTYPE *b, FTYPE *c) { 524 | FTYPE reversed_denom = 1 / ((x1 - x2) * (x1 - x3) * (x2 - x3)); 525 | 526 | *a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) * reversed_denom; 527 | *b = (x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1) + x1 * x1 * (y2 - y3)) * 528 | reversed_denom; 529 | *c = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + 530 | x1 * x2 * (x1 - x2) * y3) * 531 | reversed_denom; 532 | } 533 | 534 | uint8_t arduinoFFT::Exponent(uint16_t value) { 535 | //#warning("This method may not be accessible on future revisions.") 536 | // Calculates the base 2 logarithm of a value 537 | uint8_t result = 0; 538 | while (((value >> result) & 1) != 1) 539 | result++; 540 | return (result); 541 | } 542 | 543 | // Private functions 544 | 545 | void arduinoFFT::Swap(FTYPE *x, FTYPE *y) { 546 | FTYPE temp = *x; 547 | *x = *y; 548 | *y = temp; 549 | } 550 | 551 | -------------------------------------------------------------------------------- /src/siddisplay.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * SIDDisplay Classes: Handles the SID LEDs 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | #include "sid_global.h" 53 | 54 | #include 55 | #include 56 | 57 | #include "siddisplay.h" 58 | 59 | #include "sid_font.h" 60 | 61 | static const uint16_t translator[10][20][2] = 62 | { 63 | { 64 | { 8+2, 1<<3 }, // bar 0, top most LED { index in buffer [0-7 chip1, 8-15 chip2], bitmask } 65 | { 8+2, 1<<2 }, 66 | { 8+2, 1<<1 }, 67 | { 8+2, 1<<0 }, 68 | { 0, 1<<15 }, 69 | { 0, 1<<14 }, 70 | { 0, 1<<13 }, 71 | { 0, 1<<12 }, 72 | { 0, 1<<11 }, 73 | { 0, 1<<10 }, 74 | { 0, 1<<9 }, 75 | { 0, 1<<8 }, 76 | { 0, 1<<7 }, 77 | { 0, 1<<6 }, 78 | { 0, 1<<5 }, 79 | { 0, 1<<4 }, 80 | { 0, 1<<3 }, 81 | { 0, 1<<2 }, 82 | { 0, 1<<1 }, 83 | { 0, 1<<0 } // bar 0, bottom LED 84 | }, 85 | { 86 | { 8+3, 1<<3 }, // bar 1, top most LED 87 | { 8+3, 1<<2 }, 88 | { 8+3, 1<<1 }, 89 | { 8+3, 1<<0 }, 90 | { 1, 1<<15 }, 91 | { 1, 1<<14 }, 92 | { 1, 1<<13 }, 93 | { 1, 1<<12 }, 94 | { 1, 1<<11 }, 95 | { 1, 1<<10 }, 96 | { 1, 1<<9 }, 97 | { 1, 1<<8 }, 98 | { 1, 1<<7 }, 99 | { 1, 1<<6 }, 100 | { 1, 1<<5 }, 101 | { 1, 1<<4 }, 102 | { 1, 1<<3 }, 103 | { 1, 1<<2 }, 104 | { 1, 1<<1 }, 105 | { 1, 1<<0 } 106 | }, 107 | { 108 | { 8+4, 1<<3 }, // bar 2, top most LED 109 | { 8+4, 1<<2 }, 110 | { 8+4, 1<<1 }, 111 | { 8+4, 1<<0 }, 112 | { 2, 1<<15 }, 113 | { 2, 1<<14 }, 114 | { 2, 1<<13 }, 115 | { 2, 1<<12 }, 116 | { 2, 1<<11 }, 117 | { 2, 1<<10 }, 118 | { 2, 1<<9 }, 119 | { 2, 1<<8 }, 120 | { 2, 1<<7 }, 121 | { 2, 1<<6 }, 122 | { 2, 1<<5 }, 123 | { 2, 1<<4 }, 124 | { 2, 1<<3 }, 125 | { 2, 1<<2 }, 126 | { 2, 1<<1 }, 127 | { 2, 1<<0 } 128 | }, 129 | { 130 | { 8+5, 1<<3 }, // bar 3, top most LED 131 | { 8+5, 1<<2 }, 132 | { 8+5, 1<<1 }, 133 | { 8+5, 1<<0 }, 134 | { 3, 1<<15 }, 135 | { 3, 1<<14 }, 136 | { 3, 1<<13 }, 137 | { 3, 1<<12 }, 138 | { 3, 1<<11 }, 139 | { 3, 1<<10 }, 140 | { 3, 1<<9 }, 141 | { 3, 1<<8 }, 142 | { 3, 1<<7 }, 143 | { 3, 1<<6 }, 144 | { 3, 1<<5 }, 145 | { 3, 1<<4 }, 146 | { 3, 1<<3 }, 147 | { 3, 1<<2 }, 148 | { 3, 1<<1 }, 149 | { 3, 1<<0 } 150 | }, 151 | { 152 | { 8+6, 1<<3 }, // bar 4, top most LED 153 | { 8+6, 1<<2 }, 154 | { 8+6, 1<<1 }, 155 | { 8+6, 1<<0 }, 156 | { 4, 1<<15 }, 157 | { 4, 1<<14 }, 158 | { 4, 1<<13 }, 159 | { 4, 1<<12 }, 160 | { 4, 1<<11 }, 161 | { 4, 1<<10 }, 162 | { 4, 1<<9 }, 163 | { 4, 1<<8 }, 164 | { 4, 1<<7 }, 165 | { 4, 1<<6 }, 166 | { 4, 1<<5 }, 167 | { 4, 1<<4 }, 168 | { 4, 1<<3 }, 169 | { 4, 1<<2 }, 170 | { 4, 1<<1 }, 171 | { 4, 1<<0 } 172 | }, 173 | { 174 | { 8+7, 1<<3 }, // bar 5, top most LED 175 | { 8+7, 1<<2 }, 176 | { 8+7, 1<<1 }, 177 | { 8+7, 1<<0 }, 178 | { 5, 1<<15 }, 179 | { 5, 1<<14 }, 180 | { 5, 1<<13 }, 181 | { 5, 1<<12 }, 182 | { 5, 1<<11 }, 183 | { 5, 1<<10 }, 184 | { 5, 1<<9 }, 185 | { 5, 1<<8 }, 186 | { 5, 1<<7 }, 187 | { 5, 1<<6 }, 188 | { 5, 1<<5 }, 189 | { 5, 1<<4 }, 190 | { 5, 1<<3 }, 191 | { 5, 1<<2 }, 192 | { 5, 1<<1 }, 193 | { 5, 1<<0 } 194 | }, 195 | { 196 | { 8+2, 1<<7 }, // bar 6, top most LED 197 | { 8+2, 1<<6 }, 198 | { 8+2, 1<<5 }, 199 | { 8+2, 1<<4 }, 200 | { 6, 1<<15 }, 201 | { 6, 1<<14 }, 202 | { 6, 1<<13 }, 203 | { 6, 1<<12 }, 204 | { 6, 1<<11 }, 205 | { 6, 1<<10 }, 206 | { 6, 1<<9 }, 207 | { 6, 1<<8 }, 208 | { 6, 1<<7 }, 209 | { 6, 1<<6 }, 210 | { 6, 1<<5 }, 211 | { 6, 1<<4 }, 212 | { 6, 1<<3 }, 213 | { 6, 1<<2 }, 214 | { 6, 1<<1 }, 215 | { 6, 1<<0 } 216 | }, 217 | { 218 | { 8+3, 1<<7 }, // bar 7, top most LED 219 | { 8+3, 1<<6 }, 220 | { 8+3, 1<<5 }, 221 | { 8+3, 1<<4 }, 222 | { 7, 1<<15 }, 223 | { 7, 1<<14 }, 224 | { 7, 1<<13 }, 225 | { 7, 1<<12 }, 226 | { 7, 1<<11 }, 227 | { 7, 1<<10 }, 228 | { 7, 1<<9 }, 229 | { 7, 1<<8 }, 230 | { 7, 1<<7 }, 231 | { 7, 1<<6 }, 232 | { 7, 1<<5 }, 233 | { 7, 1<<4 }, 234 | { 7, 1<<3 }, 235 | { 7, 1<<2 }, 236 | { 7, 1<<1 }, 237 | { 7, 1<<0 } 238 | }, 239 | { 240 | { 8+4, 1<<7 }, // bar 8, top most LED 241 | { 8+4, 1<<6 }, 242 | { 8+4, 1<<5 }, 243 | { 8+4, 1<<4 }, 244 | { 8+0, 1<<15 }, 245 | { 8+0, 1<<14 }, 246 | { 8+0, 1<<13 }, 247 | { 8+0, 1<<12 }, 248 | { 8+0, 1<<11 }, 249 | { 8+0, 1<<10 }, 250 | { 8+0, 1<<9 }, 251 | { 8+0, 1<<8 }, 252 | { 8+0, 1<<7 }, 253 | { 8+0, 1<<6 }, 254 | { 8+0, 1<<5 }, 255 | { 8+0, 1<<4 }, 256 | { 8+0, 1<<3 }, 257 | { 8+0, 1<<2 }, 258 | { 8+0, 1<<1 }, 259 | { 8+0, 1<<0 } 260 | }, 261 | { 262 | { 8+5, 1<<7 }, // bar 9, top most LED 263 | { 8+5, 1<<6 }, 264 | { 8+5, 1<<5 }, 265 | { 8+5, 1<<4 }, 266 | { 8+1, 1<<15 }, 267 | { 8+1, 1<<14 }, 268 | { 8+1, 1<<13 }, 269 | { 8+1, 1<<12 }, 270 | { 8+1, 1<<11 }, 271 | { 8+1, 1<<10 }, 272 | { 8+1, 1<<9 }, 273 | { 8+1, 1<<8 }, 274 | { 8+1, 1<<7 }, 275 | { 8+1, 1<<6 }, 276 | { 8+1, 1<<5 }, 277 | { 8+1, 1<<4 }, 278 | { 8+1, 1<<3 }, 279 | { 8+1, 1<<2 }, 280 | { 8+1, 1<<1 }, 281 | { 8+1, 1<<0 } 282 | } 283 | }; 284 | 285 | #define SID_SIG_DURATION 2000 286 | 287 | static const uint16_t sigMaps[SID_SS_MAX] = { 288 | 0b1000000000, // TCD-keypad remote control mode start 289 | 0b0000000001, // TCD-keypad remote control mode end 290 | 0b1000000001 // Bad IR input 291 | }; 292 | 293 | // Store i2c address and display ID 294 | sidDisplay::sidDisplay(uint8_t address1, uint8_t address2) 295 | { 296 | _address[0] = address1; 297 | _address[1] = address2; 298 | } 299 | 300 | // Start the display 301 | void sidDisplay::begin() 302 | { 303 | directCmd(0x20 | 1); // turn on oscillator 304 | 305 | clearBuf(); // clear buffer 306 | setBrightness(15); // setup initial brightness 307 | clearDisplayDirect(); // clear display RAM 308 | on(); // turn it on 309 | } 310 | 311 | // Turn on the display 312 | void sidDisplay::on() 313 | { 314 | directCmd(0x80 | 1); 315 | } 316 | 317 | // Turn off the display 318 | void sidDisplay::off() 319 | { 320 | directCmd(0x80); 321 | } 322 | 323 | void sidDisplay::lampTest() 324 | { 325 | for(int j = 0; j < 2; j++) { 326 | Wire.beginTransmission(_address[j]); 327 | Wire.write(0x00); // start address 328 | for(int i = 0; i < SD_BUF_SIZE / 2; i++) { 329 | Wire.write(0xff); 330 | Wire.write(0xff); 331 | } 332 | Wire.endTransmission(); 333 | } 334 | } 335 | 336 | 337 | // Clear the buffer 338 | void sidDisplay::clearBuf() 339 | { 340 | for(int i = 0; i < SD_BUF_SIZE; i++) { 341 | _displayBuffer[i] = 0; 342 | } 343 | } 344 | 345 | // Set display brightness 346 | // Valid brightness levels are 0 to 15. 347 | // 255 sets it to previous level 348 | uint8_t sidDisplay::setBrightness(uint8_t level, bool setInitial) 349 | { 350 | if(level == 255) 351 | level = _brightness; // restore to old val 352 | 353 | _brightness = setBrightnessDirect(level); 354 | 355 | if(setInitial) _origBrightness = _brightness; 356 | 357 | return _brightness; 358 | } 359 | 360 | void sidDisplay::resetBrightness() 361 | { 362 | _brightness = setBrightnessDirect(_origBrightness); 363 | } 364 | 365 | uint8_t sidDisplay::setBrightnessDirect(uint8_t level) 366 | { 367 | if(level > 15) 368 | level = 15; 369 | 370 | directCmd(0xe0 | level); 371 | 372 | return level; 373 | } 374 | 375 | uint8_t sidDisplay::getBrightness() 376 | { 377 | return _brightness; 378 | } 379 | 380 | // Draw bar into buffer, do NOT call show 381 | void sidDisplay::drawBarWithHeight(uint8_t bar, uint8_t height) 382 | { 383 | // Clear bar 384 | // Draw bar with given height 385 | 386 | if(height > 127) height = 0; 387 | if(height > 20) height = 20; 388 | 389 | if(height < 20) { 390 | for(int i = 0; i < 20 - height; i++) { 391 | _displayBuffer[translator[bar][i][0]] &= ~(translator[bar][i][1]); 392 | } 393 | } 394 | if(height > 0) { 395 | for(int i = 20 - height; i < 20; i++) { 396 | _displayBuffer[translator[bar][i][0]] |= translator[bar][i][1]; 397 | } 398 | } 399 | } 400 | 401 | // Draw bar into buffer, do NOT call show 402 | void sidDisplay::drawBar(uint8_t bar, uint8_t bottom, uint8_t top) 403 | { 404 | // Clear bar 405 | // Draw bar from top to bottom (0-19, 0=bottom) 406 | 407 | if(top > 19) top = 19; 408 | if(bottom > 19) bottom = 19; 409 | if(bottom > top) bottom = top; 410 | 411 | if(top < 19) { 412 | for(int i = 0; i <= 19-top; i++) { 413 | _displayBuffer[translator[bar][i][0]] &= ~(translator[bar][i][1]); 414 | } 415 | } 416 | if(bottom > 0) { 417 | for(int i = 19; i <= 19-bottom; i--) { 418 | _displayBuffer[translator[bar][i][0]] &= ~(translator[bar][i][1]); 419 | } 420 | } 421 | for(int i = 19-top; i <= 19-bottom; i++) { 422 | _displayBuffer[translator[bar][i][0]] |= translator[bar][i][1]; 423 | } 424 | } 425 | 426 | void sidDisplay::clearBar(uint8_t bar) 427 | { 428 | for(int i = 0; i <= 19; i++) { 429 | _displayBuffer[translator[bar][i][0]] &= ~(translator[bar][i][1]); 430 | } 431 | } 432 | 433 | // Draw dot into buffer, do NOT call show 434 | void sidDisplay::drawDot(uint8_t bar, uint8_t dot_y) 435 | { 436 | // Do not clear bar 437 | // Draw dot at dot_y (0 = bottom) 438 | if(dot_y > 19) dot_y = 19; 439 | 440 | _displayBuffer[translator[bar][19-dot_y][0]] |= translator[bar][19-dot_y][1]; 441 | } 442 | 443 | void sidDisplay::drawFieldAndShow(uint8_t *fieldData) 444 | { 445 | // Draw entire field. Data is 0 or 1, organized in lines 446 | for(int i = 0, k = 0; i < 20; i++, k += 10) { 447 | for(int j = 0; j < 10; j++) { 448 | if(fieldData[k+j]) { 449 | _displayBuffer[translator[j][i][0]] |= translator[j][i][1]; 450 | } else { 451 | _displayBuffer[translator[j][i][0]] &= ~(translator[j][i][1]); 452 | } 453 | } 454 | } 455 | show(); 456 | } 457 | 458 | void sidDisplay::drawLetterAndShow(char alpha, int x, int y) 459 | { 460 | uint8_t field[20*10] = { 0 }; 461 | int w = 10, h = 10, fx = 0, fy = 0, a = 0x200, s; 462 | 463 | if(x < -9 || x > 9 || y < -9 || y > 19) { 464 | clearDisplayDirect(); 465 | return; 466 | } 467 | 468 | if(alpha >= '0' && alpha <= '9') { 469 | alpha -= '0'; 470 | } else if(alpha >= 'A' && alpha <= 'Z') { 471 | alpha -= ('A' - 10); 472 | } else if(alpha >= 'a' && alpha <= 'z') { 473 | alpha -= ('a' - 10); 474 | } else if(alpha == '.') { 475 | alpha = 36; 476 | } else if(alpha == '&') { 477 | alpha = 37; 478 | } else if(alpha == '*') { 479 | alpha = 38; 480 | } else if(alpha == '#') { 481 | alpha = 39; 482 | } else if(alpha == '^') { 483 | alpha = 40; 484 | } else if(alpha == '$') { 485 | alpha = 41; 486 | } else if(alpha == '<') { 487 | alpha = 42; 488 | } else if(alpha == '>') { 489 | alpha = 43; 490 | } else if(alpha == '~') { 491 | alpha = 44; 492 | } else { 493 | clearDisplayDirect(); 494 | return; 495 | } 496 | 497 | if(x < 0) { 498 | fx = -x; 499 | a >>= fx; 500 | w = 10 - fx; 501 | x = 0; 502 | } else if(x > 0) { 503 | w = 10 - x; 504 | } 505 | if(y < 0) { 506 | fy = -y; 507 | h = 10 - fy; 508 | y = 0; 509 | } else if(y > (20-10)) { 510 | h = 20 - y; 511 | } 512 | 513 | for(int yy = fy; yy < h; yy++, y++) { 514 | uint16_t font = alphaChars[alpha][yy]; 515 | int xxx = x; 516 | for(int xx = fx, s = a; xx < w; xx++, s >>= 1, xxx++) { 517 | if(font & s) { 518 | field[(y*10) + xxx] = 1; 519 | } 520 | } 521 | } 522 | drawFieldAndShow(field); 523 | } 524 | 525 | void sidDisplay::drawLetterMask(char alpha, int x, int y) 526 | { 527 | int w = 8, h = 8, fx = 0, fy = 0, a = 0x80, s; 528 | 529 | if(x < -7 || x > 9 || y < -7 || y > 19) { 530 | return; 531 | } 532 | 533 | if(alpha >= '0' && alpha <= '9') { 534 | alpha -= '0'; 535 | } else if(alpha >= 'A' && alpha <= 'Z') { 536 | alpha -= ('A' - 10); 537 | } else if(alpha >= 'a' && alpha <= 'z') { 538 | alpha -= ('a' - 10); 539 | } else if(alpha == '.') { 540 | alpha = 36; 541 | } else if(alpha == '#') { 542 | alpha = 37; 543 | } else if(alpha >= '$' && alpha <= '\'') { 544 | alpha -= '$'; 545 | alpha += 38; 546 | } else { 547 | return; 548 | } 549 | 550 | if(x < 0) { 551 | fx = -x; 552 | a >>= fx; 553 | w = 8 - fx; 554 | x = 0; 555 | } else if(x > 2) { 556 | w = 8 - x; 557 | } 558 | if(y < 0) { 559 | fy = -y; 560 | h = 8 - fy; 561 | y = 0; 562 | } else if(y > (20-8)) { 563 | h = 20 - y; 564 | } 565 | 566 | for(int yy = fy; yy < h; yy++, y++) { 567 | uint8_t font = alphaChars8[alpha][yy]; 568 | int xxx = x; 569 | for(int xx = fx, s = a; xx < w; xx++, s >>= 1, xxx++) { 570 | if(font & s) { 571 | _displayBuffer[translator[xxx][y][0]] &= ~(translator[xxx][y][1]); 572 | } 573 | } 574 | } 575 | } 576 | 577 | void sidDisplay::drawClockAndShow(uint8_t *dateBuf, int dx, int dy) 578 | { 579 | uint8_t field[20*10] = { 0 }; 580 | uint8_t fields[11*9] = { 0 }; 581 | int x[4], y[4], nums[4]; 582 | int ampm = -1; 583 | uint8_t t = dateBuf[4]; 584 | int s, c, cx, h, w, ox, oy, yyy; 585 | 586 | if(dx < -9 || dy < -11 || dx > 9 || dy > 19) { 587 | clearDisplayDirect(); 588 | return; 589 | } 590 | 591 | x[0] = x[2] = 0; x[1] = x[3] = 5; 592 | y[0] = y[1] = 0; y[2] = y[3] = 6; 593 | if(!(dateBuf[7] & 0x80)) { 594 | ampm = (t > 11) ? 0 : 1; 595 | if(!t) t = 12; 596 | else if(t > 12) t -= 12; 597 | } 598 | nums[0] = t / 10; 599 | nums[1] = t % 10; 600 | nums[2] = dateBuf[5] / 10; 601 | nums[3] = dateBuf[5] % 10; 602 | 603 | for(c = 0; c < 4; c++) { 604 | for(int yy = y[c], yyy = 0; yy < y[c] + 5; yy++, yyy++) { 605 | uint8_t font = numChars4[nums[c]][yyy]; 606 | for(int xx = x[c], s = 0x08; xx < x[c] + 4; xx++, s >>= 1) { 607 | if(font & s) { 608 | fields[(yy*9) + xx] = 1; 609 | } 610 | } 611 | } 612 | } 613 | 614 | if(dx < 0) { 615 | ox = -dx; 616 | w = 9 - ox; 617 | dx = 0; 618 | } else { 619 | ox = 0; 620 | w = min((10 - dx), 9); 621 | } 622 | if(dy < 0) { 623 | oy = -dy; 624 | h = 11 - oy; 625 | dy = 0; 626 | } else { 627 | oy = 0; 628 | h = min((20-dy), 11); 629 | } 630 | 631 | for(int yy = dy, c = oy; c < h; yy++, c++) { 632 | for(int xx = dx, cx = ox; cx < w; xx++, cx++) { 633 | field[(yy*10)+xx] = fields[(c*9)+cx]; 634 | } 635 | } 636 | 637 | drawFieldAndShow(field); 638 | } 639 | 640 | void sidDisplay::superImposeSpecSig() 641 | { 642 | uint16_t sigMap = sigMaps[_specialSig - 1]; 643 | 644 | for(int i = 0; i < 10; i++) { 645 | if(sigMap & (1 << i)) { 646 | _displayBuffer[translator[i][0][0]] |= translator[i][0][1]; 647 | } else { 648 | _displayBuffer[translator[i][0][0]] &= ~translator[i][0][1]; 649 | } 650 | // Clear second row to make clearer 651 | //_displayBuffer[translator[i][1][0]] &= ~translator[i][1][1]; 652 | // ... or better yet, set it 653 | _displayBuffer[translator[i][1][0]] |= translator[i][1][1]; 654 | } 655 | } 656 | 657 | // Show the buffer 658 | void sidDisplay::show() 659 | { 660 | uint16_t *tp = &_displayBuffer[0]; 661 | 662 | if(_specialSig) { 663 | if(millis() - _specialSigNow < SID_SIG_DURATION) { 664 | superImposeSpecSig(); 665 | } else { 666 | _specialSig = 0; 667 | _specialTrigger = false; 668 | } 669 | } 670 | 671 | for(int j = 0; j < 2; j++) { 672 | Wire.beginTransmission(_address[j]); 673 | Wire.write(0x00); 674 | for(int i = 0; i < SD_BUF_SIZE / 2; i++) { 675 | uint16_t t = *tp++; 676 | Wire.write(t & 0xff); 677 | Wire.write(t >> 8); 678 | } 679 | Wire.endTransmission(); 680 | } 681 | } 682 | 683 | void sidDisplay::specialSig(uint8_t sig) 684 | { 685 | if(!sig || sig > SID_SS_MAX) 686 | return; 687 | 688 | _specialSig = sig; 689 | _specialSigNow = millis(); 690 | _specialTrigger = true; 691 | } 692 | 693 | bool sidDisplay::specialTrigger() 694 | { 695 | if(!_specialTrigger) return false; 696 | 697 | if(_specialSig && (millis() - _specialSigNow > SID_SIG_DURATION)) { 698 | _specialSig = 0; 699 | _specialTrigger = false; 700 | return true; 701 | } 702 | 703 | _specialTrigger = false; 704 | return true; 705 | } 706 | 707 | void sidDisplay::clearDisplayDirect() 708 | { 709 | for(int j = 0; j < 2; j++) { 710 | Wire.beginTransmission(_address[j]); 711 | Wire.write(0x00); 712 | for(int i = 0; i < SD_BUF_SIZE / 2; i++) { 713 | Wire.write(0x00); 714 | Wire.write(0x00); 715 | } 716 | Wire.endTransmission(); 717 | } 718 | } 719 | 720 | void sidDisplay::directCmd(uint8_t val) 721 | { 722 | for(int j = 0; j < 2; j++) { 723 | Wire.beginTransmission(_address[j]); 724 | Wire.write(val); 725 | Wire.endTransmission(); 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /src/src/WiFiManager/wm_strings_en.h: -------------------------------------------------------------------------------- 1 | /** 2 | * wm_strings_en.h 3 | * 4 | * Based on: 5 | * WiFiManager, a library for the ESP32/Arduino platform 6 | * Creator tzapu (tablatronix) 7 | * Version 2.0.15 8 | * License MIT 9 | * 10 | * Adapted by Thomas Winischhofer (A10001986) 11 | */ 12 | 13 | #ifndef _WM_STRINGS_EN_H_ 14 | #define _WM_STRINGS_EN_H_ 15 | 16 | const char HTTP_HEAD_START[] PROGMEM = "" 17 | "" 18 | "" 19 | "" 20 | "" 21 | "{v}"; 22 | 23 | const char HTTP_SCRIPT[] PROGMEM = ""; 30 | 31 | const char HTTP_HEAD_END[] PROGMEM = "
"; 32 | 33 | const char HTTP_ROOT_MAIN[] PROGMEM = "

{t}

{v}

"; 34 | 35 | const char * const HTTP_PORTAL_MENU[] PROGMEM = { 36 | "
\n", 37 | "
\n", 38 | "
\n", 39 | "
\n", 40 | "
", // sep 41 | "" // custom, if _customMenuHTML is NULL 42 | }; 43 | 44 | const char HTTP_FORM_START[] PROGMEM = "
"; 45 | const char HTTP_FORM_LABEL[] PROGMEM = ""; 46 | const char HTTP_FORM_PARAM_HEAD[] PROGMEM = "
"; 47 | const char HTTP_FORM_PARAM[] PROGMEM = ""; 48 | const char HTTP_FORM_END[] PROGMEM = "
"; 49 | 50 | const char HTTP_FORM_WIFI[] PROGMEM = "
WiFi connection: Network selection





"; 51 | const char HTTP_FORM_WIFI_END[] PROGMEM = "
"; 52 | const char HTTP_WIFI_ITEM[] PROGMEM = "
{v}{c}
"; 53 | const char HTTP_FORM_SECT_HEAD[] PROGMEM = "
WiFi connection: Static IP settings
"; 54 | const char HTTP_FORM_SECT_FOOT[] PROGMEM = "
"; 55 | const char HTTP_FORM_WIFI_PH[] PROGMEM = "placeholder='Leave this section empty to use DHCP'"; 56 | const char HTTP_MSG_NONETWORKS[] PROGMEM = "
No networks found. Click 'WiFi Scan' to re-scan.
"; 57 | const char HTTP_MSG_SCANFAIL[] PROGMEM = "
Scan failed. Click 'WiFi Scan' to retry.
"; 58 | const char HTTP_MSG_NOSCAN[] PROGMEM = "
Device busy, WiFi scan prohibited.
Click 'WiFi Scan' after animation sequence has finished.
"; 59 | const char HTTP_SCAN_LINK[] PROGMEM = "
"; 60 | const char HTTP_ERASE_BUTTON[] PROGMEM = "
"; 61 | const char HTTP_SHOWALL[] PROGMEM = "
"; 62 | const char HTTP_SHOWALL_FORM[] PROGMEM = "
"; 63 | 64 | const char HTTP_PARAMSAVED[] PROGMEM = "
Settings saved. Rebooting.
"; 65 | const char HTTP_SAVED_NORMAL[] PROGMEM = "Trying to connect to network.
In case of error, device boots in AP mode."; 66 | const char HTTP_SAVED_CARMODE[] PROGMEM = "
Device is run in car mode and will not
connect to WiFi network after reboot."; 67 | const char HTTP_SAVED_ERASED[] PROGMEM = "WiFi network credentials deleted.
Restarting in AP mode.
"; 68 | const char HTTP_PARAMSAVED_END[] PROGMEM = "
"; 69 | 70 | const char HTTP_UPDATE[] PROGMEM = "
Upload new firmware

"; 71 | const char HTTP_UPLOADSND1[] PROGMEM = "
Upload sound pack ("; 72 | const char HTTP_UPLOADSND2[] PROGMEM = ".bin)
and/or .mp3 file(s)

"; 73 | const char HTTP_UPDATE_FAIL1[] PROGMEM = "
Update failed.
"; 74 | const char HTTP_UPDATE_FAIL2[] PROGMEM = "
"; 75 | const char HTTP_UPDATE_SUCCESS[] PROGMEM = "
Update successful.
Device rebooting.
"; 76 | const char HTTP_UPLOAD_SDMSG[] PROGMEM = "
In order to upload the sound-pack,
please insert an SD card.
"; 77 | 78 | const char HTTP_STATUS_HEAD[] PROGMEM = "
"; 79 | const char HTTP_STATUS_TAIL[] PROGMEM = "
"; 80 | const char HTTP_STATUS_ON[] PROGMEM = "{v}
{i}"; 81 | const char HTTP_STATUS_OFF[] PROGMEM = "{v}
{r}Operating in {V}mode"; 82 | const char HTTP_STATUS_OFFNOAP[] PROGMEM = "Network not found
"; // WL_NO_SSID_AVAIL 83 | const char HTTP_STATUS_OFFFAIL[] PROGMEM = "Failed to connect
"; // WL_CONNECT_FAILED 84 | const char HTTP_STATUS_DISCONN[] PROGMEM = "Disconnected. Wrong Password?
"; // WL_DISCONNECTED 85 | const char HTTP_STATUS_APMODE[] PROGMEM = "AP-"; 86 | const char HTTP_STATUS_CARMODE[] PROGMEM = "car "; 87 | const char HTTP_STATUS_NONE[] PROGMEM = "
No WiFi connection configured
"; 88 | 89 | const char HTTP_BR[] PROGMEM = "
"; 90 | const char HTTP_END[] PROGMEM = "
"; 91 | const char HTML_CHKBOX[] PROGMEM = "type='checkbox' autocomplete='off'"; // ac=off to fix FF idiocy 92 | 93 | const char HTTP_STYLE[] PROGMEM = ""; 125 | 126 | const char HTTP_STYLE_MSG[] PROGMEM = ""; 131 | 132 | // quality icons plus some specific JS 133 | const char HTTP_STYLE_QI[] PROGMEM = "" 150 | ""; 155 | 156 | const char A_paramsave[] PROGMEM = "paramsave"; 157 | const char A_param2save[] PROGMEM = "param2save"; 158 | const char A_wifisave[] PROGMEM = "wifisave"; 159 | 160 | const char S_titlewifi[] PROGMEM = "WiFi Configuration"; 161 | const char S_titleparam[] PROGMEM = "Settings"; 162 | #ifdef WM_PARAM2 163 | const char S_titleparam2[] PROGMEM = WM_PARAM2_TITLE; 164 | #endif 165 | const char S_titleupd[] PROGMEM = "Upload"; 166 | 167 | const char S_passph[] PROGMEM = "********"; 168 | const char S_staticip[] PROGMEM = "Static IP"; 169 | const char S_staticgw[] PROGMEM = "Static gateway"; 170 | const char S_staticdns[] PROGMEM = "Static DNS"; 171 | const char S_subnet[] PROGMEM = "Subnet mask"; 172 | 173 | const char S_brand[] PROGMEM = "WiFiManager"; 174 | 175 | const char S_GET[] PROGMEM = "GET"; 176 | const char S_POST[] PROGMEM = "POST"; 177 | const char S_NA[] PROGMEM = "Unknown"; 178 | 179 | const char S_nonprintable[] PROGMEM = "[Non-printable SSID]"; 180 | 181 | const char S_notfound[] PROGMEM = "404 File not found\n\n"; 182 | 183 | // Routes 184 | const char R_root[] PROGMEM = "/"; 185 | const char R_wifi[] PROGMEM = "/wifi"; 186 | const char R_wifisave[] PROGMEM = "/wifisave"; 187 | const char R_param[] PROGMEM = "/param"; 188 | const char R_paramsave[] PROGMEM = "/paramsave"; 189 | #ifdef WM_PARAM2 190 | const char R_param2[] PROGMEM = "/param2"; 191 | const char R_param2save[] PROGMEM = "/param2save"; 192 | #endif 193 | const char R_update[] PROGMEM = "/update"; 194 | const char R_updatedone[] PROGMEM = "/u"; 195 | 196 | // Strings 197 | const char S_ip[] PROGMEM = WMS_ip; 198 | const char S_gw[] PROGMEM = WMS_gw; 199 | const char S_sn[] PROGMEM = WMS_sn; 200 | const char S_dns[] PROGMEM = WMS_dns; 201 | 202 | // Tokens 203 | const char T_v[] PROGMEM = "{v}"; // @token v 204 | const char T_V[] PROGMEM = "{V}"; // @token v 205 | const char T_I[] PROGMEM = "{I}"; // @token I 206 | const char T_i[] PROGMEM = "{i}"; // @token i 207 | const char T_n[] PROGMEM = "{n}"; // @token n 208 | const char T_p[] PROGMEM = "{p}"; // @token p 209 | const char T_t[] PROGMEM = "{t}"; // @token t 210 | const char T_l[] PROGMEM = "{l}"; // @token l 211 | const char T_c[] PROGMEM = "{c}"; // @token c 212 | const char T_e[] PROGMEM = "{e}"; // @token e 213 | const char T_q[] PROGMEM = "{q}"; // @token q 214 | const char T_r[] PROGMEM = "{r}"; // @token r 215 | const char T_R[] PROGMEM = "{R}"; // @token R 216 | const char T_h[] PROGMEM = "{h}"; // @token h 217 | const char T_f[] PROGMEM = "{f}"; // @token f 218 | 219 | // http 220 | const char HTTP_HEAD_CL[] PROGMEM = "Content-Length"; 221 | const char HTTP_HEAD_CT[] PROGMEM = "text/html"; 222 | const char HTTP_HEAD_CT2[] PROGMEM = "text/plain"; 223 | const char HTTP_HEAD_CORS[] PROGMEM = "Access-Control-Allow-Origin"; 224 | const char HTTP_HEAD_CORS_ALLOW_ALL[] PROGMEM = "*"; 225 | 226 | // Debug 227 | #ifdef _A10001986_DBG 228 | const char * const WIFI_STA_STATUS[] PROGMEM = 229 | { 230 | "WL_IDLE_STATUS", // 0 STATION_IDLE 231 | "WL_NO_SSID_AVAIL", // 1 STATION_NO_AP_FOUND 232 | "WL_SCAN_COMPLETED", // 2 233 | "WL_CONNECTED", // 3 STATION_GOT_IP 234 | "WL_CONNECT_FAILED", // 4 STATION_CONNECT_FAIL, STATION_WRONG_PASSWORD(NI) 235 | "WL_CONNECTION_LOST", // 5 236 | "WL_DISCONNECTED", // 6 237 | "WL_STATION_WRONG_PASSWORD" // 7 KLUDGE 238 | }; 239 | const char* const WIFI_MODES[] PROGMEM = { "NULL", "STA", "AP", "STA+AP" }; 240 | #endif 241 | 242 | #endif // _WM_STRINGS_EN_H_ 243 | -------------------------------------------------------------------------------- /src/src/WiFiManager/WiFiManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * WiFiManager.h 3 | * 4 | * Based on: 5 | * WiFiManager, a library for the ESP32/Arduino platform 6 | * Creator tzapu (tablatronix) 7 | * Version 2.0.15 8 | * License MIT 9 | * 10 | * Adapted by Thomas Winischhofer (A10001986) 11 | */ 12 | 13 | 14 | #ifndef WiFiManager_h 15 | #define WiFiManager_h 16 | 17 | #include "wm_local.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #ifndef WEBSERVER_H 24 | #include 25 | #endif 26 | 27 | #ifdef WM_MDNS 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | 34 | // prep string concat vars 35 | #define WM_STRING2(x) #x 36 | #define WM_STRING(x) WM_STRING2(x) 37 | 38 | // menu ids 39 | #define WM_MENU_WIFI 0 40 | #define WM_MENU_PARAM 1 41 | #define WM_MENU_PARAM2 2 42 | #define WM_MENU_UPDATE 3 43 | #define WM_MENU_SEP 4 44 | #define WM_MENU_CUSTOM 5 45 | #define WM_MENU_END -1 46 | #define WM_MENU_MAX WM_MENU_CUSTOM 47 | 48 | #ifdef WM_PARAM2 49 | #define WM_PARAM_ARRS 3 50 | #else 51 | #define WM_PARAM_ARRS 2 52 | #endif 53 | 54 | #ifndef WM_PARAM2_CAPTION 55 | #define WM_PARAM2_CAPTION "Settings 2" 56 | #endif 57 | #ifndef WM_PARAM2_TITLE 58 | #define WM_PARAM2_TITLE "Settings 2" 59 | #endif 60 | 61 | // params will autoincrement and realloc by this amount when max is reached 62 | // can (and should) be overruled by allocParms()/allocWiFiParms() 63 | #ifndef WIFI_MANAGER_MAX_PARAMS 64 | #define WIFI_MANAGER_MAX_PARAMS 5 65 | #endif 66 | 67 | // Flags: 68 | // Label placement for parameters 69 | #define WFM_NO_LABEL 0 70 | #define WFM_LABEL_BEFORE 1 71 | #define WFM_LABEL_AFTER 2 72 | #define WFM_LABEL_DEFAULT WFM_LABEL_BEFORE 73 | #define WFM_LABEL_MASK 0x03 74 | // Parm is a checkbox 75 | #define WFM_IS_CHKBOX 8 76 | #define WFM_NO_BR 16 77 | 78 | // HTML id:s of "static IP" parameters on "WiFi Configuration" page 79 | #define WMS_ip "ip" 80 | #define WMS_gw "gw" 81 | #define WMS_sn "sn" 82 | #define WMS_dns "dns" 83 | 84 | // Parm handed to GPCallback 85 | #define WM_LP_NONE 0 // No special reason (just do over-due stuff) 86 | #define WM_LP_PREHTTPSEND 1 // pre-HTTPSend() (problem with mp3 in AP mode) 87 | #define WM_LP_POSTHTTPSEND 2 // post-HTTPSend() (just do over-due stuff, ...) 88 | 89 | #define WM_WIFI_SCAN_BUSY -133 90 | 91 | #define DNS_PORT 53 92 | 93 | #define MAX_SCAN_OUTPUT_SIZE 6144 // Maximum buffer for scan list on WiFi Config page 94 | 95 | #if defined(ESP_ARDUINO_VERSION) && defined(ESP_ARDUINO_VERSION_VAL) 96 | #if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2,0,0) 97 | #define WM_NOCOUNTRY 98 | #endif 99 | //#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2,0,5) 100 | #define WM_DISCONWORKAROUND 101 | //#endif 102 | #else 103 | #define WM_NOCOUNTRY 104 | #endif 105 | 106 | class WiFiManagerParameter { 107 | public: 108 | /** 109 | Create custom parameters that can be added to the WiFiManager setup web page 110 | @id is used for HTTP queries and must not contain spaces nor other special characters 111 | */ 112 | WiFiManagerParameter(const char *custom); 113 | WiFiManagerParameter(const char *(*CustomHTMLGenerator)(const char *)); 114 | //WiFiManagerParameter(const char *id, const char *label); 115 | WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length); 116 | WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom); 117 | WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom, uint8_t flags); 118 | ~WiFiManagerParameter(); 119 | 120 | const char *getID() const; 121 | const char *getValue() const; 122 | const char *getLabel() const; 123 | int getValueLength() const; 124 | uint8_t getFlags() const; 125 | virtual const char *getCustomHTML() const; 126 | void setValue(const char *defaultValue, int length); 127 | 128 | protected: 129 | void init(const char *id, const char *label, const char *defaultValue, int length, const char *custom, uint8_t flags); 130 | 131 | private: 132 | WiFiManagerParameter& operator=(const WiFiManagerParameter&); 133 | const char *_id; 134 | const char *_label; 135 | union { 136 | char *_value; 137 | const char *(*_customHTMLGenerator)(const char *); 138 | }; 139 | int _length; 140 | uint8_t _flags; 141 | protected: 142 | const char *_customHTML; 143 | friend class WiFiManager; 144 | }; 145 | 146 | 147 | class WiFiManager 148 | { 149 | ///////////////////////////////////////////////////////////////////////////// 150 | // Public // 151 | ///////////////////////////////////////////////////////////////////////////// 152 | 153 | public: 154 | WiFiManager(); 155 | ~WiFiManager(); 156 | void WiFiManagerInit(); 157 | 158 | // auto connect to saved wifi, or custom, and start config portal on failures 159 | bool autoConnect(const char *ssid, const char *pass, const char *apName, const char *apPassword = NULL); 160 | 161 | // manually start the config portal (AP mode) 162 | bool startConfigPortal(const char *apName, const char *apPassword = NULL, 163 | const char *ssid = NULL, const char *pass = NULL); 164 | 165 | // manually stop the config portal if started manually, stop immediatly 166 | bool stopConfigPortal(); 167 | 168 | // manually start the web portal (STA/connected) (=same as Config Portal in AP mode) 169 | void startWebPortal(); 170 | 171 | // manually stop the web portal if started manually (used for when connected) 172 | void stopWebPortal(); 173 | 174 | // Run webserver processing. Param: Do handle webserver, or skip 175 | void process(bool handleWeb = true); 176 | 177 | // disconnect wifi 178 | bool disconnect(); 179 | 180 | // Disable WiFi all together (result: WiFi mode = WIFI_OFF) 181 | void disableWiFi(); 182 | 183 | // allocate numParms entries in params array (overrules WIFI_MANAGER_MAX_PARAMS) 184 | void allocParms(int numParms); 185 | 186 | // adds a custom parameter, returns false on failure 187 | bool addParameter(WiFiManagerParameter *p); 188 | 189 | // returns the list of Parameters 190 | WiFiManagerParameter** getParameters(); 191 | 192 | // returns the Parameters Count 193 | int getParametersCount(); 194 | 195 | // same as above for param2 196 | #ifdef WM_PARAM2 197 | void allocParms2(int numParms); 198 | bool addParameter2(WiFiManagerParameter *p); 199 | WiFiManagerParameter** getParameters2(); 200 | int getParameters2Count(); 201 | #endif 202 | 203 | // same as above for WiFi Configuration page 204 | void allocWiFiParms(int numParms); 205 | bool addWiFiParameter(WiFiManagerParameter *p); 206 | WiFiManagerParameter** getWiFiParameters(); 207 | int getWiFiParametersCount(); 208 | 209 | // SET CALLBACKS 210 | 211 | // called after AP mode and config portal has started 212 | #ifdef WM_APCALLBACK 213 | void setAPCallback(void(*func)(WiFiManager*)); 214 | #endif 215 | 216 | // called after wifi hw init, but before connection attempts 217 | #ifdef WM_PRECONNECTCB 218 | void setPreConnectCallback(void(*func)()); 219 | #endif 220 | 221 | #ifdef WM_EVENTCB 222 | void setWiFiEventCallback(void(*func)(WiFiEvent_t event)); 223 | #endif 224 | 225 | // called after webserver has started 226 | void setWebServerCallback(void(*func)()); 227 | 228 | // called when saving params-in-wifi before anything else happens 229 | void setPreSaveWiFiCallback(void(*func)()); 230 | 231 | // called when wifi settings have been read from the webform 232 | void setSaveWiFiCallback(void(*func)(const char *, const char *)); 233 | 234 | // called when saving params before anything else happens 235 | #ifdef WM_PRESAVECB 236 | void setPreSaveParamsCallback(void(*func)(int)); 237 | #endif 238 | 239 | // called when saving either params-in-wifi or params page 240 | void setSaveParamsCallback(void(*func)(int)); 241 | 242 | // called just before/after OTA update 243 | void setPreOtaUpdateCallback(void(*func)()); 244 | void setPostOtaUpdateCallback(void(*func)(bool)); 245 | 246 | // add stuff to the main menu; second one to give WM the length for buf sizing 247 | void setMenuOutCallback(void(*func)(String &page)); 248 | void setMenuOutLenCallback(int(*func)()); 249 | 250 | // app-specific replacement for delay() 251 | void setDelayReplacement(void(*func)(unsigned int)); 252 | 253 | // called to give app chance to update stuff when WM op might take a while 254 | // WM_LP_xxx given as arg 255 | void setGPCallback(void(*func)(int)); 256 | 257 | // Pre-scan, allows app to forbid scan (use cache, or display no list) 258 | void setPreWiFiScanCallback(bool(*func)()); 259 | 260 | // Set connection parameters 261 | 262 | // sets timeout for which to attempt connecting, useful if you get a lot of failed connects 263 | void setConnectTimeout(unsigned long seconds); 264 | 265 | // sets number of retries for autoconnect, force retry after wait failure exit 266 | void setConnectRetries(uint8_t numRetries); // default 1 267 | 268 | // set min rssi to include in scan, defaults to -80 if not specified 269 | #ifdef WM_ADDLSETTERS 270 | void setMinimumRSSI(int rssi = -80); 271 | #endif 272 | 273 | // sets a custom ip /gateway /subnet configuration 274 | #ifdef WM_AP_STATIC_IP 275 | void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn); 276 | #endif 277 | 278 | // sets config for a static IP 279 | void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn); 280 | 281 | // sets config for a static IP with DNS 282 | void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn, IPAddress dns); 283 | 284 | // set a custom hostname, sets sta and ap dhcp client id for esp32 285 | // Given hostname is not copied but referenced by pointer 286 | void setHostname(const char * hostname); 287 | 288 | // set ap channel 289 | void setWiFiAPChannel(int32_t channel); 290 | 291 | // set ap max clients 292 | void setWiFiAPMaxClients(int max); // default 4 293 | 294 | // clean connect, always disconnect before connecting 295 | #ifdef WM_ADDLSETTERS 296 | void setCleanConnect(bool enable); // default true 297 | #endif 298 | 299 | // set port of webserver, 80 300 | #ifdef WM_ADDLSETTERS 301 | void setHttpPort(uint16_t port); 302 | #endif 303 | 304 | // CONFIG PORTAL 305 | 306 | // set custom menu items and order 307 | // docopy can be false if the menu array is a global const (that does not 308 | // get destructed) that also has WM_MENU_END at the end. 309 | void setMenu(const int8_t *menu, uint8_t size, bool doCopy = true); 310 | 311 | // set the webapp title, default WiFiManager 312 | // Given title is not copied but referenced by pointer 313 | void setTitle(const char *title); 314 | 315 | // show audio upload on Update page 316 | void showUploadContainer(bool enable, const char *contName, bool showMsg = false); 317 | 318 | // Custom 319 | void setCarMode(bool enable); 320 | 321 | // add custom html at inside for all pages 322 | void setCustomHeadElement(const char* html); 323 | 324 | // if this is set, customise style 325 | void setCustomMenuHTML(const char* html); 326 | 327 | // if true, always show static net inputs, IP, subnet, gateway, else only show if set via setSTAStaticIPConfig 328 | #ifdef WM_ADDLSETTERS 329 | void setShowStaticFields(bool alwaysShow); 330 | #endif 331 | 332 | // if true, always show static dns, esle only show if set via setSTAStaticIPConfig 333 | #ifdef WM_ADDLSETTERS 334 | void setShowDnsFields(bool alwaysShow); 335 | #endif 336 | 337 | // get last connection result, includes autoconnect and wifisave 338 | #ifdef WM_ADDLGETTERS 339 | uint8_t getLastConxResult(); 340 | #endif 341 | 342 | // gets number of retries for autoconnect, force retry after wait failure exit 343 | uint8_t getConnectRetries(); 344 | 345 | // make some HTML templates available for app 346 | const char * getHTTPSTART(int& titleStart); 347 | const char * getHTTPSCRIPT(); 348 | const char * getHTTPSTYLE(); 349 | const char * getHTTPSTYLEOK(); 350 | 351 | // check if config portal is active (true) 352 | #ifdef WM_ADDLGETTERS 353 | bool getConfigPortalActive(); 354 | #endif 355 | 356 | // check if web portal is active (true) 357 | bool getWebPortalActive(); 358 | 359 | // get hostname helper 360 | String getWiFiHostname(); 361 | 362 | bool getBestAPChannel(int32_t& channel, int& quality); 363 | 364 | // Transitional function to read out the NVS-stored credentials 365 | void getStoredCredentials(char *ssid, size_t slen, char *pass, size_t plen); 366 | 367 | std::unique_ptr dnsServer; 368 | 369 | std::unique_ptr server; 370 | 371 | ///////////////////////////////////////////////////////////////////////////// 372 | // Private // 373 | ///////////////////////////////////////////////////////////////////////////// 374 | 375 | private: 376 | // vars 377 | int8_t * _menuIdArr = NULL; 378 | bool _menuArrConst = false; 379 | 380 | // ip configs 381 | #ifdef WM_AP_STATIC_IP 382 | IPAddress _ap_static_ip; 383 | IPAddress _ap_static_gw; 384 | IPAddress _ap_static_sn; 385 | #endif 386 | IPAddress _sta_static_ip; 387 | IPAddress _sta_static_gw; 388 | IPAddress _sta_static_sn; 389 | IPAddress _sta_static_dns; 390 | 391 | uint8_t _lastconxresult = WL_IDLE_STATUS; // store last result when doing connect operations 392 | int _numNetworks = 0; 393 | int16_t _numNetworksAsync = 0; 394 | unsigned long _lastscan = 0; // ms for timing wifi scans 395 | unsigned long _bestChCacheTime = 0; 396 | uint16_t _bestChCache = 0; 397 | 398 | // SSIDs and passwords 399 | char _apName[34] = ""; 400 | char _apPassword[66] = ""; 401 | char _ssid[34] = ""; // currently used ssid 402 | char _pass[66] = ""; // currently used psk 403 | 404 | const char * _hostname = ""; // hostname for dhcp, and/or MDNS 405 | 406 | const char * _title = NULL; // app title 407 | 408 | // options & flags 409 | unsigned long _connectTimeout = 0; // ms stop trying to connect to ap if set 410 | 411 | bool _cleanConnect = true; // disconnect before connect in connectwifi, increases stability on connects 412 | #if 0 413 | bool _disableSTA = false; // disable sta when starting ap, always 414 | bool _disableSTAConn = true; // disable sta when starting ap, if sta is not connected ( stability ) 415 | #endif 416 | int32_t _apChannel = 0; // default channel to use for ap, 0 for auto 417 | int _ap_max_clients = 4; // softap max clients 418 | uint16_t _httpPort = 80; // port for webserver 419 | uint8_t _connectRetries = 1; // number of sta connect retries, force reconnect, wait loop (connectimeout) does not always work and first disconnect bails 420 | 421 | wifi_event_id_t wm_event_id = 0; 422 | static uint8_t _eventlastconxresult; // for wifi event callback 423 | static uint16_t _WiFiEventMask; // for wifi event callback 424 | bool _wifiOffFlag = false; 425 | 426 | int _minimumRSSI = -1000; // filter wifiscan ap by this rssi 427 | bool _staShowStaticFields = true; 428 | bool _staShowDns = true; 429 | 430 | bool _showUploadSnd = false; // Show upload audio on Update page 431 | bool _showContMsg = false; 432 | char _sndContName[8] = ""; // File name of BIN file to upload 433 | 434 | const char * _customHeadElement = NULL; // store custom head element html from user inside 435 | const char * _customMenuHTML = NULL; // store custom element html from user inside menu 436 | 437 | // internal options 438 | unsigned int _scancachetime = 30000; // ms cache time for preload scans 439 | 440 | bool _autoforcerescan = false; // automatically force rescan if scan networks is 0, ignoring cache 441 | 442 | bool _carMode = false; // Custom 443 | 444 | bool _hasBegun = false; // flag wm loaded,unloaded 445 | 446 | void _begin(); 447 | void _end(); 448 | 449 | void _delay(unsigned int mydel); 450 | 451 | bool CheckParmID(const char *id); 452 | bool _addParameter(int idx, WiFiManagerParameter *p); 453 | 454 | void setupConfigPortal(); 455 | bool shutdownConfigPortal(); 456 | 457 | bool startAP(); 458 | 459 | void setupDNSD(); 460 | void setupHTTPServer(); 461 | void setupMDNS(); 462 | 463 | uint8_t connectWifi(const char *ssid, const char *pass); 464 | bool setStaticConfig(); 465 | bool wifiConnectNew(const char *ssid, const char *pass); 466 | 467 | uint8_t waitForConnectResult(bool haveStatic); 468 | uint8_t waitForConnectResult(bool haveStatic, uint32_t timeout); 469 | 470 | bool wifiSTAOn(); 471 | bool wifiSTAOff(); 472 | bool waitEvent(uint16_t mask, unsigned long timeout); 473 | 474 | // webserver handlers 475 | unsigned int getHTTPHeadLength(const char *title, bool includeMSG = false, bool includeQI = false); 476 | void getHTTPHeadNew(String& page, const char *title, bool includeMSG = false, bool includeQI = false); 477 | 478 | unsigned int getParamOutSize(WiFiManagerParameter** params, 479 | int paramsCount, unsigned int& maxItemSize); 480 | void getParamOut(String &page, WiFiManagerParameter** params, 481 | int paramsCount, unsigned int maxItemSize); 482 | void doParamSave(WiFiManagerParameter** params, int paramsCount); 483 | 484 | int reportStatusLen(); 485 | void reportStatus(String &page, unsigned int estSize = 0); 486 | 487 | void HTTPSend(const String &content); 488 | 489 | // Root menu 490 | int getMenuOutLength(); 491 | void getMenuOut(String& page); 492 | unsigned int calcRootLen(unsigned int& headSize, unsigned int& repSize); 493 | void buildRootPage(String& page, unsigned int headSize, unsigned int repSize); 494 | void handleRoot(); 495 | 496 | // WiFi page 497 | int getScanItemStart(); 498 | void sortNetworks(int n, int *indices, int& haveDupes, bool removeDupes); 499 | unsigned int getScanItemsLen(int n, bool scanErr, int *indices, unsigned int& maxItemSize, int& stopAt, bool showall); 500 | void getScanItemsOut(String& page, int n, bool scanErr, int *indices, unsigned int maxItemSize, bool showall); 501 | void getIpForm(String& page, const char *id, const char *title, IPAddress& value, const char *ph = NULL); 502 | void getStaticOut(String& page); 503 | unsigned int getStaticLen(); 504 | void buildWifiPage(String& page, bool scan); 505 | void handleWifi(bool scan); 506 | void handleWifiSave(); 507 | 508 | // Param page 509 | int calcParmPageSize(int aidx, unsigned int& maxItemSize, const char *title, const char *action); 510 | void _handleParam(int aidx, const char *title, const char *action); 511 | void _handleParamSave(int aidx, const char *title); 512 | void handleParam(); 513 | void handleParamSave(); 514 | #ifdef WM_PARAM2 515 | void handleParam2(); 516 | void handleParam2Save(); 517 | #endif 518 | 519 | // OTA Update page 520 | void handleUpdate(); 521 | void handleUpdating(); 522 | void handleUpdateDone(); 523 | 524 | // Other 525 | void handleNotFound(); 526 | 527 | void processConfigPortal(bool handleWeb); 528 | 529 | // wifi platform abstractions 530 | void WiFi_installEventHandler(); 531 | String WiFi_SSID() const; 532 | 533 | int16_t WiFi_scanNetworks(bool force, bool async); 534 | int16_t WiFi_waitForScan(); 535 | void WiFi_scanComplete(int16_t networksFound); 536 | 537 | void WiFiEvent(WiFiEvent_t event, arduino_event_info_t info); 538 | 539 | // helper for html 540 | String htmlEntities(String& str, bool forprint = false); 541 | int htmlEntitiesLen(String& str, bool forprint = false); 542 | bool checkSSID(String& ssid); 543 | 544 | long wmmap(long x); 545 | 546 | // get default ap esp uses, esp_chipid 547 | void getDefaultAPName(char *apname); 548 | 549 | // internal version 550 | bool _getbestapchannel(int32_t& channel, int& quality); 551 | 552 | // reboot esp32 553 | void reboot(); 554 | 555 | // flags 556 | bool configPortalActive = false; 557 | bool webPortalActive = false; 558 | 559 | // WiFiManagerParameter 560 | int _paramsCount[WM_PARAM_ARRS] = { 0 }; 561 | int _max_params[WM_PARAM_ARRS]; 562 | WiFiManagerParameter** _params[WM_PARAM_ARRS] = { NULL }; 563 | 564 | bool _uplError = false; 565 | 566 | // callbacks 567 | #ifdef WM_APCALLBACK 568 | void (*_apcallback)(WiFiManager*); 569 | #endif 570 | void (*_webservercallback)(void); 571 | void (*_savewificallback)(const char *, const char *); 572 | void (*_presavewificallback)(void); 573 | #ifdef WM_PRESAVECB 574 | void (*_presaveparamscallback)(int); 575 | #endif 576 | void (*_saveparamscallback)(int); 577 | void (*_preotaupdatecallback)(void); 578 | void (*_postotaupdatecallback)(bool); 579 | void (*_menuoutcallback)(String &page); 580 | int (*_menuoutlencallback)(void); 581 | void (*_delayreplacement)(unsigned int); 582 | void (*_gpcallback)(int); 583 | bool (*_prewifiscancallback)(void); 584 | #ifdef WM_PRECONNECTCB 585 | void (*_preconnectcallback)(void); 586 | #endif 587 | #ifdef WM_EVENTCB 588 | void (_wifieventcallback)(WiFiEvent_t event); 589 | #endif 590 | 591 | #ifdef _A10001986_DBG 592 | // get a status as string 593 | String getWLStatusString(uint8_t status); 594 | // get wifi mode as string 595 | String getModeString(uint8_t mode); 596 | // debug output the softap config 597 | void debugSoftAPConfig(); 598 | #endif 599 | }; 600 | 601 | #endif 602 | -------------------------------------------------------------------------------- /src/sid_font.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------------- 3 | * CircuitSetup.us Status Indicator Display 4 | * (C) 2023-2025 Thomas Winischhofer (A10001986) 5 | * https://github.com/realA10001986/SID 6 | * https://sid.out-a-ti.me 7 | * 8 | * Fonts 9 | * 10 | * ------------------------------------------------------------------- 11 | * License: MIT NON-AI 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without restriction, 16 | * including without limitation the rights to use, copy, modify, 17 | * merge, publish, distribute, sublicense, and/or sell copies of the 18 | * Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * In addition, the following restrictions apply: 25 | * 26 | * 1. The Software and any modifications made to it may not be used 27 | * for the purpose of training or improving machine learning algorithms, 28 | * including but not limited to artificial intelligence, natural 29 | * language processing, or data mining. This condition applies to any 30 | * derivatives, modifications, or updates based on the Software code. 31 | * Any usage of the Software in an AI-training dataset is considered a 32 | * breach of this License. 33 | * 34 | * 2. The Software may not be included in any dataset used for 35 | * training or improving machine learning algorithms, including but 36 | * not limited to artificial intelligence, natural language processing, 37 | * or data mining. 38 | * 39 | * 3. Any person or organization found to be in violation of these 40 | * restrictions will be subject to legal action and may be held liable 41 | * for any damages resulting from such use. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 46 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 47 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 48 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 49 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | */ 51 | 52 | 53 | #ifndef _SID_FONT_H 54 | #define _SID_FONT_H 55 | 56 | static const uint16_t alphaChars[36+1+1+7][10] = 57 | { 58 | { 59 | 0b0011111100, 60 | 0b0111111110, 61 | 0b1110000111, 62 | 0b1100000011, 63 | 0b1100000011, 64 | 0b1100000011, 65 | 0b1100000011, 66 | 0b1110000111, 67 | 0b0111111110, 68 | 0b0011111100 69 | }, 70 | { 71 | 0b0000110000, 72 | 0b0001110000, 73 | 0b0011110000, 74 | 0b0000110000, 75 | 0b0000110000, 76 | 0b0000110000, 77 | 0b0000110000, 78 | 0b0000110000, 79 | 0b0011111100, 80 | 0b0011111100 81 | }, 82 | { 83 | 0b1111111100, 84 | 0b1111111110, 85 | 0b0000000111, 86 | 0b0000000111, 87 | 0b0011111110, 88 | 0b0111111100, 89 | 0b1110000000, 90 | 0b1100000000, 91 | 0b1111111111, 92 | 0b1111111111 93 | }, 94 | { 95 | 0b1111111100, 96 | 0b1111111110, 97 | 0b0000000111, 98 | 0b0000000111, 99 | 0b0001111110, 100 | 0b0001111110, 101 | 0b0000000111, 102 | 0b0000000111, 103 | 0b1111111110, 104 | 0b1111111100 105 | }, 106 | { 107 | 0b0000011111, 108 | 0b0000111111, 109 | 0b0001110011, 110 | 0b0011100011, 111 | 0b0111000011, 112 | 0b1111111111, 113 | 0b1111111111, 114 | 0b0000000011, 115 | 0b0000000011, 116 | 0b0000000011 117 | }, 118 | { 119 | 0b1111111110, 120 | 0b1111111110, 121 | 0b1100000000, 122 | 0b1100000000, 123 | 0b1111111100, 124 | 0b1111111110, 125 | 0b0000000111, 126 | 0b0000000111, 127 | 0b1111111110, 128 | 0b1111111100 129 | }, 130 | { 131 | 0b0011111110, 132 | 0b0111111110, 133 | 0b1110000000, 134 | 0b1100000000, 135 | 0b1111111100, 136 | 0b1111111110, 137 | 0b1100000111, 138 | 0b1110000111, 139 | 0b0111111110, 140 | 0b0011111100 141 | }, 142 | { 143 | 0b1111111111, 144 | 0b1111111111, 145 | 0b0000000011, 146 | 0b0000000111, 147 | 0b0000001110, 148 | 0b0000011100, 149 | 0b0000111000, 150 | 0b0000110000, 151 | 0b0000110000, 152 | 0b0000110000 153 | }, 154 | { 155 | 0b0011111100, 156 | 0b0111111110, 157 | 0b1100000011, 158 | 0b1100000011, 159 | 0b0111111110, 160 | 0b0111111110, 161 | 0b1100000011, 162 | 0b1100000011, 163 | 0b0111111110, 164 | 0b0011111100 165 | }, 166 | { 167 | 0b0011111100, 168 | 0b0111111110, 169 | 0b1100000011, 170 | 0b1100000011, 171 | 0b0111111111, 172 | 0b0111111111, 173 | 0b0000000011, 174 | 0b0000000011, 175 | 0b0111111110, 176 | 0b0011111100 177 | }, 178 | { 179 | 0b0011111100, 180 | 0b0111111110, 181 | 0b1110000111, 182 | 0b1100000011, 183 | 0b1100000011, 184 | 0b1111111111, 185 | 0b1111111111, 186 | 0b1100000011, 187 | 0b1100000011, 188 | 0b1100000011 189 | }, 190 | { 191 | 0b1111111100, 192 | 0b1111111110, 193 | 0b1100000111, 194 | 0b1100000111, 195 | 0b1111111100, 196 | 0b1111111100, 197 | 0b1100000111, 198 | 0b1100000111, 199 | 0b1111111110, 200 | 0b1111111100 201 | }, 202 | { 203 | 0b0011111100, 204 | 0b0111111110, 205 | 0b1110000111, 206 | 0b1100000000, 207 | 0b1100000000, 208 | 0b1100000000, 209 | 0b1100000000, 210 | 0b1110000111, 211 | 0b0111111110, 212 | 0b0011111100 213 | }, 214 | { 215 | 0b1111111100, 216 | 0b1111111110, 217 | 0b1100000111, 218 | 0b1100000011, 219 | 0b1100000011, 220 | 0b1100000011, 221 | 0b1100000011, 222 | 0b1100000111, 223 | 0b1111111110, 224 | 0b1111111100 225 | }, 226 | { 227 | 0b1111111111, 228 | 0b1111111111, 229 | 0b1100000000, 230 | 0b1100000000, 231 | 0b1111110000, 232 | 0b1111110000, 233 | 0b1100000000, 234 | 0b1100000000, 235 | 0b1111111111, 236 | 0b1111111111, 237 | }, 238 | { 239 | 0b1111111111, 240 | 0b1111111111, 241 | 0b1100000000, 242 | 0b1100000000, 243 | 0b1111110000, 244 | 0b1111110000, 245 | 0b1100000000, 246 | 0b1100000000, 247 | 0b1100000000, 248 | 0b1100000000 249 | }, 250 | { 251 | 0b0011111110, 252 | 0b0111111111, 253 | 0b1110000011, 254 | 0b1100000000, 255 | 0b1100111111, 256 | 0b1100111111, 257 | 0b1100000011, 258 | 0b1110000011, 259 | 0b0111111111, 260 | 0b0011111111 261 | }, 262 | { 263 | 0b1100000011, 264 | 0b1100000011, 265 | 0b1100000011, 266 | 0b1100000011, 267 | 0b1111111111, 268 | 0b1111111111, 269 | 0b1100000011, 270 | 0b1100000011, 271 | 0b1100000011, 272 | 0b1100000011 273 | }, 274 | { 275 | 0b0111111110, 276 | 0b0111111110, 277 | 0b0000110000, 278 | 0b0000110000, 279 | 0b0000110000, 280 | 0b0000110000, 281 | 0b0000110000, 282 | 0b0000110000, 283 | 0b0111111110, 284 | 0b0111111110 285 | }, 286 | { 287 | 0b0011111111, 288 | 0b0011111111, 289 | 0b0000000011, 290 | 0b0000000011, 291 | 0b0000000011, 292 | 0b0000000011, 293 | 0b1100000011, 294 | 0b1110000111, 295 | 0b0111111110, 296 | 0b0011111100 297 | }, 298 | { 299 | 0b1100000111, 300 | 0b1100001110, 301 | 0b1100011100, 302 | 0b1100111000, 303 | 0b1111110000, 304 | 0b1111110000, 305 | 0b1100111000, 306 | 0b1100011100, 307 | 0b1100001110, 308 | 0b1100000111 309 | }, 310 | { 311 | 0b1100000000, 312 | 0b1100000000, 313 | 0b1100000000, 314 | 0b1100000000, 315 | 0b1100000000, 316 | 0b1100000000, 317 | 0b1100000000, 318 | 0b1100000000, 319 | 0b1111111111, 320 | 0b1111111111 321 | }, 322 | { 323 | 0b1100000011, 324 | 0b1110000111, 325 | 0b1111001111, 326 | 0b1111111111, 327 | 0b1101111011, 328 | 0b1100110011, 329 | 0b1100000011, 330 | 0b1100000011, 331 | 0b1100000011, 332 | 0b1100000011 333 | }, 334 | { 335 | 0b1100000011, 336 | 0b1110000011, 337 | 0b1111000011, 338 | 0b1111100011, 339 | 0b1101110011, 340 | 0b1100111011, 341 | 0b1100011111, 342 | 0b1100001111, 343 | 0b1100000111, 344 | 0b1100000011 345 | }, 346 | { 347 | 0b0011111100, 348 | 0b0111111110, 349 | 0b1110000111, 350 | 0b1100000011, 351 | 0b1100000011, 352 | 0b1100000011, 353 | 0b1100000011, 354 | 0b1110000111, 355 | 0b0111111110, 356 | 0b0011111100 357 | }, 358 | { 359 | 0b1111111100, 360 | 0b1111111110, 361 | 0b1100000111, 362 | 0b1100000111, 363 | 0b1111111110, 364 | 0b1111111100, 365 | 0b1100000000, 366 | 0b1100000000, 367 | 0b1100000000, 368 | 0b1100000000 369 | }, 370 | { 371 | 0b0011111100, 372 | 0b0111111110, 373 | 0b1110000111, 374 | 0b1100000011, 375 | 0b1100000011, 376 | 0b1100000111, 377 | 0b1100001110, 378 | 0b1110011100, 379 | 0b0111111111, 380 | 0b0011111111 381 | }, 382 | { 383 | 0b1111111100, 384 | 0b1111111110, 385 | 0b1100000111, 386 | 0b1100000111, 387 | 0b1111111110, 388 | 0b1111111110, 389 | 0b1100001110, 390 | 0b1100000111, 391 | 0b1100000011, 392 | 0b1100000011 393 | }, 394 | { 395 | 0b0011111111, 396 | 0b0111111111, 397 | 0b1110000000, 398 | 0b1110000000, 399 | 0b0111111100, 400 | 0b0011111110, 401 | 0b0000000111, 402 | 0b0000000111, 403 | 0b1111111110, 404 | 0b1111111100 405 | }, 406 | { 407 | 0b1111111111, 408 | 0b1111111111, 409 | 0b0000110000, 410 | 0b0000110000, 411 | 0b0000110000, 412 | 0b0000110000, 413 | 0b0000110000, 414 | 0b0000110000, 415 | 0b0000110000, 416 | 0b0000110000 417 | }, 418 | { 419 | 0b1100000011, 420 | 0b1100000011, 421 | 0b1100000011, 422 | 0b1100000011, 423 | 0b1100000011, 424 | 0b1100000011, 425 | 0b1100000011, 426 | 0b1110000111, 427 | 0b0111001110, 428 | 0b0011111100 429 | }, 430 | { 431 | 0b1100000011, 432 | 0b1100000011, 433 | 0b1100000011, 434 | 0b1100000011, 435 | 0b1100000011, 436 | 0b1110000111, 437 | 0b0111001110, 438 | 0b0011111100, 439 | 0b0001111000, 440 | 0b0000110000 441 | }, 442 | { 443 | 0b1100000011, 444 | 0b1100000011, 445 | 0b1100000011, 446 | 0b1100000011, 447 | 0b1100110011, 448 | 0b1101111011, 449 | 0b1111111111, 450 | 0b1111001111, 451 | 0b1110000111, 452 | 0b1100000011 453 | }, 454 | { 455 | 0b1100000011, 456 | 0b1110000111, 457 | 0b0111001110, 458 | 0b0011111100, 459 | 0b0001111000, 460 | 0b0001111000, 461 | 0b0011111100, 462 | 0b0111001110, 463 | 0b1110000111, 464 | 0b1100000011 465 | }, 466 | { 467 | 0b1100000011, 468 | 0b1110000111, 469 | 0b0111001110, 470 | 0b0011111100, 471 | 0b0001111000, 472 | 0b0000110000, 473 | 0b0000110000, 474 | 0b0000110000, 475 | 0b0000110000, 476 | 0b0000110000 477 | }, 478 | { 479 | 0b1111111111, 480 | 0b1111111111, 481 | 0b0000001110, 482 | 0b0000011100, 483 | 0b0000111000, 484 | 0b0001110000, 485 | 0b0011100000, 486 | 0b0111000000, 487 | 0b1111111111, 488 | 0b1111111111 489 | }, 490 | { 491 | 0b0000000000, 492 | 0b0000000000, 493 | 0b0000000000, 494 | 0b0000000000, 495 | 0b0000000000, 496 | 0b0000000000, 497 | 0b0000000000, 498 | 0b0000000000, 499 | 0b0000110000, 500 | 0b0000110000 501 | }, 502 | { // 37 503 | 0b1111111111, 504 | 0b0110000110, 505 | 0b0011001100, 506 | 0b0001111000, 507 | 0b0000110000, 508 | 0b0001111000, 509 | 0b0011001100, 510 | 0b0110000110, 511 | 0b1111111111, 512 | 0b0000000000 513 | }, 514 | { 515 | 0b0000000000, 516 | 0b0110000110, 517 | 0b0011001100, 518 | 0b0001111000, 519 | 0b1111111111, 520 | 0b0001111000, 521 | 0b0011001100, 522 | 0b0110000110, 523 | 0b0000000000, 524 | 0b0000000000 525 | }, 526 | { 527 | 0b0011001100, 528 | 0b0011001100, 529 | 0b0011001100, 530 | 0b1111111111, 531 | 0b0011001100, 532 | 0b0011001100, 533 | 0b1111111111, 534 | 0b0011001100, 535 | 0b0011001100, 536 | 0b0011001100 537 | }, 538 | { 539 | 0b0000110000, 540 | 0b0001111000, 541 | 0b0011111100, 542 | 0b0111111110, 543 | 0b0000110000, 544 | 0b0000110000, 545 | 0b0000110000, 546 | 0b0000110000, 547 | 0b0000110000, 548 | 0b0000110000 549 | }, 550 | { 551 | 0b0000110000, 552 | 0b0000110000, 553 | 0b0000110000, 554 | 0b0000110000, 555 | 0b0000110000, 556 | 0b0000110000, 557 | 0b0111111110, 558 | 0b0011111100, 559 | 0b0001111000, 560 | 0b0000110000 561 | }, 562 | { 563 | 0b0000100000, 564 | 0b0001100000, 565 | 0b0011100000, 566 | 0b0111100000, 567 | 0b1111111111, 568 | 0b1111111111, 569 | 0b0111100000, 570 | 0b0011100000, 571 | 0b0001100000, 572 | 0b0000100000 573 | }, 574 | { 575 | 0b0000010000, 576 | 0b0000011000, 577 | 0b0000011100, 578 | 0b0000011110, 579 | 0b1111111111, 580 | 0b1111111111, 581 | 0b0000011110, 582 | 0b0000011100, 583 | 0b0000011000, 584 | 0b0000010000 585 | }, 586 | { 587 | 0b0000000000, 588 | 0b0000000000, 589 | 0b0110010001, 590 | 0b1001010010, 591 | 0b1001011100, 592 | 0b1001010010, 593 | 0b0110010001, 594 | 0b0000000000, 595 | 0b0000000000, 596 | 0b0000000000 597 | } 598 | }; 599 | 600 | static const uint8_t alphaChars8[36+1+1+4][8] = 601 | { 602 | { 603 | 0b01111110, 604 | 0b11111111, 605 | 0b11000011, 606 | 0b11000011, 607 | 0b11000011, 608 | 0b11000011, 609 | 0b11111111, 610 | 0b01111110 611 | }, 612 | { 613 | 0b00011000, 614 | 0b01111000, 615 | 0b01111000, 616 | 0b00011000, 617 | 0b00011000, 618 | 0b00011000, 619 | 0b00011000, 620 | 0b00011000 621 | }, 622 | { 623 | 0b11111110, 624 | 0b11111111, 625 | 0b00000011, 626 | 0b00111111, 627 | 0b01111110, 628 | 0b11000000, 629 | 0b11111111, 630 | 0b11111111 631 | }, 632 | { 633 | 0b11111110, 634 | 0b11111111, 635 | 0b00000011, 636 | 0b00111111, 637 | 0b00111111, 638 | 0b00000011, 639 | 0b11111111, 640 | 0b11111110, 641 | }, 642 | { 643 | 0b00000111, 644 | 0b00001111, 645 | 0b00011011, 646 | 0b00110011, 647 | 0b01100011, 648 | 0b11111111, 649 | 0b00000011, 650 | 0b00000011 651 | }, 652 | { 653 | 0b11111111, 654 | 0b11111111, 655 | 0b11000000, 656 | 0b11111110, 657 | 0b11111111, 658 | 0b00000011, 659 | 0b11111111, 660 | 0b11111110, 661 | }, 662 | { 663 | 0b01111111, 664 | 0b11111111, 665 | 0b11000000, 666 | 0b11111110, 667 | 0b11111111, 668 | 0b11000011, 669 | 0b11111111, 670 | 0b01111110, 671 | }, 672 | { 673 | 0b11111111, 674 | 0b11111111, 675 | 0b00000011, 676 | 0b00000110, 677 | 0b00001100, 678 | 0b00011000, 679 | 0b00011000, 680 | 0b00011000 681 | }, 682 | { 683 | 0b01111110, 684 | 0b11111111, 685 | 0b11000011, 686 | 0b11111111, 687 | 0b01111110, 688 | 0b11000011, 689 | 0b11111111, 690 | 0b01111110 691 | }, 692 | { 693 | 0b01111110, 694 | 0b11111111, 695 | 0b11000011, 696 | 0b11111111, 697 | 0b01111111, 698 | 0b00000011, 699 | 0b11111111, 700 | 0b01111110 701 | }, 702 | { 703 | 0b01111110, 704 | 0b01111110, 705 | 0b11100111, 706 | 0b11000011, 707 | 0b11111111, 708 | 0b11111111, 709 | 0b11000011, 710 | 0b11000011 711 | }, 712 | { 713 | 0b11111110, 714 | 0b11111111, 715 | 0b11000011, 716 | 0b11111110, 717 | 0b11111110, 718 | 0b11000011, 719 | 0b11111111, 720 | 0b11111110 721 | }, 722 | { 723 | 0b01111110, 724 | 0b11111111, 725 | 0b11000011, 726 | 0b11000000, 727 | 0b11000000, 728 | 0b11000011, 729 | 0b11111111, 730 | 0b01111110 731 | }, 732 | { 733 | 0b11111110, 734 | 0b11111111, 735 | 0b11000011, 736 | 0b11000011, 737 | 0b11000011, 738 | 0b11000011, 739 | 0b11111111, 740 | 0b11111110 741 | }, 742 | { 743 | 0b11111111, 744 | 0b11111111, 745 | 0b11000000, 746 | 0b11111100, 747 | 0b11111100, 748 | 0b11000000, 749 | 0b11111111, 750 | 0b11111111, 751 | }, 752 | { 753 | 0b11111111, 754 | 0b11111111, 755 | 0b11000000, 756 | 0b11111100, 757 | 0b11111100, 758 | 0b11000000, 759 | 0b11000000, 760 | 0b11000000 761 | }, 762 | { 763 | 0b01111110, 764 | 0b11111111, 765 | 0b11000011, 766 | 0b11000000, 767 | 0b11001111, 768 | 0b11000011, 769 | 0b11111111, 770 | 0b01111110 771 | }, 772 | { 773 | 0b11000011, 774 | 0b11000011, 775 | 0b11000011, 776 | 0b11111111, 777 | 0b11111111, 778 | 0b11000011, 779 | 0b11000011, 780 | 0b11000011 781 | }, 782 | { 783 | 0b00111100, 784 | 0b00011000, 785 | 0b00011000, 786 | 0b00011000, 787 | 0b00011000, 788 | 0b00011000, 789 | 0b00011000, 790 | 0b00111100 791 | }, 792 | { 793 | 0b00000011, 794 | 0b00000011, 795 | 0b00000011, 796 | 0b00000011, 797 | 0b00000011, 798 | 0b11000011, 799 | 0b11111111, 800 | 0b01111110 801 | }, 802 | { 803 | 0b11000011, 804 | 0b11000110, 805 | 0b11001100, 806 | 0b11111000, 807 | 0b11111000, 808 | 0b11001100, 809 | 0b11000110, 810 | 0b11000011 811 | }, 812 | { 813 | 0b11000000, 814 | 0b11000000, 815 | 0b11000000, 816 | 0b11000000, 817 | 0b11000000, 818 | 0b11000000, 819 | 0b11111111, 820 | 0b11111111 821 | }, 822 | { 823 | 0b11000011, 824 | 0b11100111, 825 | 0b11111111, 826 | 0b11011011, 827 | 0b11000011, 828 | 0b11000011, 829 | 0b11000011, 830 | 0b11000011 831 | }, 832 | { 833 | 0b11000011, 834 | 0b11100011, 835 | 0b11110011, 836 | 0b11011011, 837 | 0b11001111, 838 | 0b11000111, 839 | 0b11000011, 840 | 0b11000011 841 | }, 842 | { 843 | 0b01111110, 844 | 0b11111111, 845 | 0b11000011, 846 | 0b11000011, 847 | 0b11000011, 848 | 0b11000011, 849 | 0b11111111, 850 | 0b01111110 851 | }, 852 | { 853 | 0b11111110, 854 | 0b11111111, 855 | 0b11000011, 856 | 0b11111111, 857 | 0b11111110, 858 | 0b11000000, 859 | 0b11000000, 860 | 0b11000000 861 | }, 862 | { 863 | 0b01111110, 864 | 0b11111111, 865 | 0b11000011, 866 | 0b11000011, 867 | 0b11000011, 868 | 0b11001101, 869 | 0b11110110, 870 | 0b01111011 871 | }, 872 | { 873 | 0b11111110, 874 | 0b11111111, 875 | 0b11000011, 876 | 0b11111110, 877 | 0b11111100, 878 | 0b11000110, 879 | 0b11000011, 880 | 0b11000011 881 | }, 882 | { 883 | 0b01111111, 884 | 0b11111111, 885 | 0b11000000, 886 | 0b01111110, 887 | 0b01111110, 888 | 0b00000011, 889 | 0b11111111, 890 | 0b11111110 891 | }, 892 | { 893 | 0b11111111, 894 | 0b11111111, 895 | 0b00011000, 896 | 0b00011000, 897 | 0b00011000, 898 | 0b00011000, 899 | 0b00011000, 900 | 0b00011000 901 | }, 902 | { 903 | 0b11000011, 904 | 0b11000011, 905 | 0b11000011, 906 | 0b11000011, 907 | 0b11000011, 908 | 0b11000011, 909 | 0b11111111, 910 | 0b01111110 911 | }, 912 | { 913 | 0b11000011, 914 | 0b11000011, 915 | 0b11000011, 916 | 0b11000011, 917 | 0b11000011, 918 | 0b01100110, 919 | 0b00111100, 920 | 0b00011000 921 | }, 922 | { 923 | 0b11000011, 924 | 0b11000011, 925 | 0b11000011, 926 | 0b11000011, 927 | 0b11011011, 928 | 0b11111111, 929 | 0b11100111, 930 | 0b11000011 931 | 932 | }, 933 | { 934 | 0b11000011, 935 | 0b11000011, 936 | 0b01100110, 937 | 0b00111100, 938 | 0b00111100, 939 | 0b01100110, 940 | 0b11000011, 941 | 0b11000011 942 | }, 943 | { 944 | 0b11000011, 945 | 0b11000011, 946 | 0b11000011, 947 | 0b01100110, 948 | 0b00111100, 949 | 0b00011000, 950 | 0b00011000, 951 | 0b00011000 952 | }, 953 | { 954 | 0b11111111, 955 | 0b11111111, 956 | 0b00000110, 957 | 0b00001100, 958 | 0b00011000, 959 | 0b00110000, 960 | 0b11111111, 961 | 0b11111111 962 | }, 963 | { 964 | 0b00000000, 965 | 0b00000000, 966 | 0b00000000, 967 | 0b00000000, 968 | 0b00000000, 969 | 0b00000000, 970 | 0b00011000, 971 | 0b00011000 972 | }, 973 | { 974 | 0 975 | }, 976 | { 977 | 0b01111110, 978 | 0b11111111, 979 | 0b11111111, 980 | 0b11111111, 981 | 0b11111111, 982 | 0b11111111, 983 | 0b11111111, 984 | 0b11100111 985 | }, 986 | { 987 | 0b11111111, 988 | 0b10111101, 989 | 0b11111111, 990 | 0b11111111, 991 | 0b11111111, 992 | 0b11111111, 993 | 0b11100111, 994 | 0b11111111 995 | }, 996 | { 997 | 0b11111111, 998 | 0b11111111, 999 | 0b11011011, 1000 | 0b11111111, 1001 | 0b11111111, 1002 | 0b11100111, 1003 | 0b11111111, 1004 | 0b11111111 1005 | }, 1006 | { 1007 | 0b11111111, 1008 | 0b11111111, 1009 | 0b11111111, 1010 | 0b11100111, 1011 | 0b11100111, 1012 | 0b11111111, 1013 | 0b11111111, 1014 | 0b11111111, 1015 | }, 1016 | }; 1017 | 1018 | static const uint8_t numChars4[10][5] = 1019 | { 1020 | { 1021 | 0b0110, 1022 | 0b1001, 1023 | 0b1001, 1024 | 0b1001, 1025 | 0b0110 1026 | }, 1027 | { 1028 | 0b0010, 1029 | 0b0110, 1030 | 0b0010, 1031 | 0b0010, 1032 | 0b0010 1033 | }, 1034 | { 1035 | 0b1110, 1036 | 0b0001, 1037 | 0b0110, 1038 | 0b1000, 1039 | 0b1111 1040 | }, 1041 | { 1042 | 0b1110, 1043 | 0b0001, 1044 | 0b0110, 1045 | 0b0001, 1046 | 0b1110 1047 | }, 1048 | { 1049 | 0b1001, 1050 | 0b1001, 1051 | 0b1111, 1052 | 0b0001, 1053 | 0b0001 1054 | }, 1055 | { 1056 | 0b1111, 1057 | 0b1000, 1058 | 0b1110, 1059 | 0b0001, 1060 | 0b1110 1061 | }, 1062 | { 1063 | 0b0111, 1064 | 0b1000, 1065 | 0b1110, 1066 | 0b1001, 1067 | 0b0110 1068 | }, 1069 | { 1070 | 0b1111, 1071 | 0b0001, 1072 | 0b0010, 1073 | 0b0100, 1074 | 0b0100 1075 | }, 1076 | { 1077 | 0b0110, 1078 | 0b1001, 1079 | 0b0110, 1080 | 0b1001, 1081 | 0b0110 1082 | }, 1083 | { 1084 | 0b0110, 1085 | 0b1001, 1086 | 0b0111, 1087 | 0b0001, 1088 | 0b1110 1089 | } 1090 | }; 1091 | #endif 1092 | --------------------------------------------------------------------------------