migrate
This commit is contained in:
commit
6230c840c6
13
.devcontainer/Dockerfile
Normal file
13
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -y && apt-get install udev -y
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
||||
19
.devcontainer/devcontainer.json
Normal file
19
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
build/
|
||||
.vscode/
|
||||
managed_components/
|
||||
dependencies.lock
|
||||
sdkconfig.old
|
||||
.cache/
|
||||
8
CMakeLists.txt
Normal file
8
CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(blk_box_control)
|
||||
53
README.md
Normal file
53
README.md
Normal file
@ -0,0 +1,53 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | ----- |
|
||||
|
||||
# Hello World Example
|
||||
|
||||
Starts a FreeRTOS task to print "Hello World".
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## How to use example
|
||||
|
||||
Follow detailed instructions provided specifically for this example.
|
||||
|
||||
Select the instructions depending on Espressif chip installed on your development board:
|
||||
|
||||
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
|
||||
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
|
||||
|
||||
|
||||
## Example folder contents
|
||||
|
||||
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
|
||||
|
||||
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
|
||||
|
||||
Below is short explanation of remaining files in the project folder.
|
||||
|
||||
```
|
||||
├── CMakeLists.txt
|
||||
├── pytest_hello_world.py Python script used for automated testing
|
||||
├── main
|
||||
│ ├── CMakeLists.txt
|
||||
│ └── hello_world_main.c
|
||||
└── README.md This is the file you are currently reading
|
||||
```
|
||||
|
||||
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
## Technical support and feedback
|
||||
|
||||
Please use the following feedback channels:
|
||||
|
||||
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
|
||||
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
|
||||
|
||||
We will get back to you as soon as possible.
|
||||
20
main/CMakeLists.txt
Normal file
20
main/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"helper.cpp"
|
||||
"main.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES
|
||||
esp_driver_gpio
|
||||
esp_driver_i2c
|
||||
esp_driver_i2s
|
||||
esp_driver_ledc
|
||||
esp_lcd
|
||||
nvs_flash
|
||||
fatfs
|
||||
)
|
||||
|
||||
add_subdirectory(drivers)
|
||||
add_subdirectory(steps)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS components)
|
||||
8
main/Kconfig.projbuild
Normal file
8
main/Kconfig.projbuild
Normal file
@ -0,0 +1,8 @@
|
||||
menu "BLK_BOX Config"
|
||||
config USE_NEW_DISPLAY
|
||||
bool "use new TFT display"
|
||||
default y
|
||||
help
|
||||
Whether or not you have a new TFT display.
|
||||
Incorrectly selecting may result in wrong colors.
|
||||
endmenu
|
||||
28
main/drivers/CMakeLists.txt
Normal file
28
main/drivers/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
set(SOURCES
|
||||
"all.cpp"
|
||||
"TM1640/TM16xx.cpp"
|
||||
"TM1640/TM1640.cpp"
|
||||
"SparkFunBQ27441/SparkFunBQ27441.cpp"
|
||||
"esp_lcd_ili9488/esp_lcd_ili9488.c"
|
||||
"bottom_half.cpp"
|
||||
"char_lcd.cpp"
|
||||
"game_info.cpp"
|
||||
"game_timer.cpp"
|
||||
"hwdata.cpp"
|
||||
"i2c_lcd_pcf8574.c"
|
||||
"i2c.cpp"
|
||||
"leds.cpp"
|
||||
"nvs.cpp"
|
||||
"perh.cpp"
|
||||
"power.cpp"
|
||||
"sd.cpp"
|
||||
"speaker.cpp"
|
||||
"sseg.cpp"
|
||||
"starcode.cpp"
|
||||
"state_tracking.cpp"
|
||||
"tft.cpp"
|
||||
"wires.cpp"
|
||||
"wlvgl.cpp"
|
||||
)
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES})
|
||||
186
main/drivers/SparkFunBQ27441/BQ27441_Definitions.h
Normal file
186
main/drivers/SparkFunBQ27441/BQ27441_Definitions.h
Normal file
@ -0,0 +1,186 @@
|
||||
// Modified or adapted from https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
//
|
||||
// Originally Licensed under the MIT
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 SparkFun Electronics, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/******************************************************************************
|
||||
BQ27441_Definitions.h
|
||||
BQ27441 LiPo Fuel Gauge Definitions
|
||||
Jim Lindblom @ SparkFun Electronics
|
||||
May 9, 2016
|
||||
https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
|
||||
BQ27441 hardware constants, register addresses, and bit positions.
|
||||
|
||||
Hardware Resources:
|
||||
- Arduino Development Board
|
||||
- SparkFun Battery Babysitter
|
||||
|
||||
Development environment specifics:
|
||||
Arduino 1.6.7
|
||||
SparkFun Battery Babysitter v1.0
|
||||
Arduino Uno (any 'duino should do)
|
||||
******************************************************************************/
|
||||
#define BQ72441_I2C_ADDRESS 0x55 // Default I2C address of the BQ27441-G1A
|
||||
|
||||
///////////////////////
|
||||
// General Constants //
|
||||
///////////////////////
|
||||
#define BQ27441_UNSEAL_KEY 0x8000 // Secret code to unseal the BQ27441-G1A
|
||||
#define BQ27441_DEVICE_ID 0x0421 // Default device ID
|
||||
|
||||
///////////////////////
|
||||
// Standard Commands //
|
||||
///////////////////////
|
||||
// The fuel gauge uses a series of 2-byte standard commands to enable system
|
||||
// reading and writing of battery information. Each command has an associated
|
||||
// sequential command-code pair.
|
||||
#define BQ27441_COMMAND_CONTROL 0x00 // Control()
|
||||
#define BQ27441_COMMAND_TEMP 0x02 // Temperature()
|
||||
#define BQ27441_COMMAND_VOLTAGE 0x04 // Voltage()
|
||||
#define BQ27441_COMMAND_FLAGS 0x06 // Flags()
|
||||
#define BQ27441_COMMAND_NOM_CAPACITY 0x08 // NominalAvailableCapacity()
|
||||
#define BQ27441_COMMAND_AVAIL_CAPACITY 0x0A // FullAvailableCapacity()
|
||||
#define BQ27441_COMMAND_REM_CAPACITY 0x0C // RemainingCapacity()
|
||||
#define BQ27441_COMMAND_FULL_CAPACITY 0x0E // FullChargeCapacity()
|
||||
#define BQ27441_COMMAND_AVG_CURRENT 0x10 // AverageCurrent()
|
||||
#define BQ27441_COMMAND_STDBY_CURRENT 0x12 // StandbyCurrent()
|
||||
#define BQ27441_COMMAND_MAX_CURRENT 0x14 // MaxLoadCurrent()
|
||||
#define BQ27441_COMMAND_AVG_POWER 0x18 // AveragePower()
|
||||
#define BQ27441_COMMAND_SOC 0x1C // StateOfCharge()
|
||||
#define BQ27441_COMMAND_INT_TEMP 0x1E // InternalTemperature()
|
||||
#define BQ27441_COMMAND_SOH 0x20 // StateOfHealth()
|
||||
#define BQ27441_COMMAND_REM_CAP_UNFL 0x28 // RemainingCapacityUnfiltered()
|
||||
#define BQ27441_COMMAND_REM_CAP_FIL 0x2A // RemainingCapacityFiltered()
|
||||
#define BQ27441_COMMAND_FULL_CAP_UNFL 0x2C // FullChargeCapacityUnfiltered()
|
||||
#define BQ27441_COMMAND_FULL_CAP_FIL 0x2E // FullChargeCapacityFiltered()
|
||||
#define BQ27441_COMMAND_SOC_UNFL 0x30 // StateOfChargeUnfiltered()
|
||||
|
||||
//////////////////////////
|
||||
// Control Sub-commands //
|
||||
//////////////////////////
|
||||
// Issuing a Control() command requires a subsequent 2-byte subcommand. These
|
||||
// additional bytes specify the particular control function desired. The
|
||||
// Control() command allows the system to control specific features of the fuel
|
||||
// gauge during normal operation and additional features when the device is in
|
||||
// different access modes.
|
||||
#define BQ27441_CONTROL_STATUS 0x00
|
||||
#define BQ27441_CONTROL_DEVICE_TYPE 0x01
|
||||
#define BQ27441_CONTROL_FW_VERSION 0x02
|
||||
#define BQ27441_CONTROL_DM_CODE 0x04
|
||||
#define BQ27441_CONTROL_PREV_MACWRITE 0x07
|
||||
#define BQ27441_CONTROL_CHEM_ID 0x08
|
||||
#define BQ27441_CONTROL_BAT_INSERT 0x0C
|
||||
#define BQ27441_CONTROL_BAT_REMOVE 0x0D
|
||||
#define BQ27441_CONTROL_SET_HIBERNATE 0x11
|
||||
#define BQ27441_CONTROL_CLEAR_HIBERNATE 0x12
|
||||
#define BQ27441_CONTROL_SET_CFGUPDATE 0x13
|
||||
#define BQ27441_CONTROL_SHUTDOWN_ENABLE 0x1B
|
||||
#define BQ27441_CONTROL_SHUTDOWN 0x1C
|
||||
#define BQ27441_CONTROL_SEALED 0x20
|
||||
#define BQ27441_CONTROL_PULSE_SOC_INT 0x23
|
||||
#define BQ27441_CONTROL_RESET 0x41
|
||||
#define BQ27441_CONTROL_SOFT_RESET 0x42
|
||||
#define BQ27441_CONTROL_EXIT_CFGUPDATE 0x43
|
||||
#define BQ27441_CONTROL_EXIT_RESIM 0x44
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Control Status Word - Bit Definitions //
|
||||
///////////////////////////////////////////
|
||||
// Bit positions for the 16-bit data of CONTROL_STATUS.
|
||||
// CONTROL_STATUS instructs the fuel gauge to return status information to
|
||||
// Control() addresses 0x00 and 0x01. The read-only status word contains status
|
||||
// bits that are set or cleared either automatically as conditions warrant or
|
||||
// through using specified subcommands.
|
||||
#define BQ27441_STATUS_SHUTDOWNEN (1<<15)
|
||||
#define BQ27441_STATUS_WDRESET (1<<14)
|
||||
#define BQ27441_STATUS_SS (1<<13)
|
||||
#define BQ27441_STATUS_CALMODE (1<<12)
|
||||
#define BQ27441_STATUS_CCA (1<<11)
|
||||
#define BQ27441_STATUS_BCA (1<<10)
|
||||
#define BQ27441_STATUS_QMAX_UP (1<<9)
|
||||
#define BQ27441_STATUS_RES_UP (1<<8)
|
||||
#define BQ27441_STATUS_INITCOMP (1<<7)
|
||||
#define BQ27441_STATUS_HIBERNATE (1<<6)
|
||||
#define BQ27441_STATUS_SLEEP (1<<4)
|
||||
#define BQ27441_STATUS_LDMD (1<<3)
|
||||
#define BQ27441_STATUS_RUP_DIS (1<<2)
|
||||
#define BQ27441_STATUS_VOK (1<<1)
|
||||
|
||||
////////////////////////////////////
|
||||
// Flag Command - Bit Definitions //
|
||||
////////////////////////////////////
|
||||
// Bit positions for the 16-bit data of Flags()
|
||||
// This read-word function returns the contents of the fuel gauging status
|
||||
// register, depicting the current operating status.
|
||||
#define BQ27441_FLAG_OT (1<<15)
|
||||
#define BQ27441_FLAG_UT (1<<14)
|
||||
#define BQ27441_FLAG_FC (1<<9)
|
||||
#define BQ27441_FLAG_CHG (1<<8)
|
||||
#define BQ27441_FLAG_OCVTAKEN (1<<7)
|
||||
#define BQ27441_FLAG_ITPOR (1<<5)
|
||||
#define BQ27441_FLAG_CFGUPMODE (1<<4)
|
||||
#define BQ27441_FLAG_BAT_DET (1<<3)
|
||||
#define BQ27441_FLAG_SOC1 (1<<2)
|
||||
#define BQ27441_FLAG_SOCF (1<<1)
|
||||
#define BQ27441_FLAG_DSG (1<<0)
|
||||
|
||||
////////////////////////////
|
||||
// Extended Data Commands //
|
||||
////////////////////////////
|
||||
// Extended data commands offer additional functionality beyond the standard
|
||||
// set of commands. They are used in the same manner; however, unlike standard
|
||||
// commands, extended commands are not limited to 2-byte words.
|
||||
#define BQ27441_EXTENDED_OPCONFIG 0x3A // OpConfig()
|
||||
#define BQ27441_EXTENDED_CAPACITY 0x3C // DesignCapacity()
|
||||
#define BQ27441_EXTENDED_DATACLASS 0x3E // DataClass()
|
||||
#define BQ27441_EXTENDED_DATABLOCK 0x3F // DataBlock()
|
||||
#define BQ27441_EXTENDED_BLOCKDATA 0x40 // BlockData()
|
||||
#define BQ27441_EXTENDED_CHECKSUM 0x60 // BlockDataCheckSum()
|
||||
#define BQ27441_EXTENDED_CONTROL 0x61 // BlockDataControl()
|
||||
|
||||
////////////////////////////////////////
|
||||
// Configuration Class, Subclass ID's //
|
||||
////////////////////////////////////////
|
||||
// To access a subclass of the extended data, set the DataClass() function
|
||||
// with one of these values.
|
||||
// Configuration Classes
|
||||
#define BQ27441_ID_SAFETY 2 // Safety
|
||||
#define BQ27441_ID_CHG_TERMINATION 36 // Charge Termination
|
||||
#define BQ27441_ID_CONFIG_DATA 48 // Data
|
||||
#define BQ27441_ID_DISCHARGE 49 // Discharge
|
||||
#define BQ27441_ID_REGISTERS 64 // Registers
|
||||
#define BQ27441_ID_POWER 68 // Power
|
||||
// Gas Gauging Classes
|
||||
#define BQ27441_ID_IT_CFG 80 // IT Cfg
|
||||
#define BQ27441_ID_CURRENT_THRESH 81 // Current Thresholds
|
||||
#define BQ27441_ID_STATE 82 // State
|
||||
// Ra Tables Classes
|
||||
#define BQ27441_ID_R_A_RAM 89 // R_a RAM
|
||||
// Calibration Classes
|
||||
#define BQ27441_ID_CALIB_DATA 104 // Data
|
||||
#define BQ27441_ID_CC_CAL 105 // CC Cal
|
||||
#define BQ27441_ID_CURRENT 107 // Current
|
||||
// Security Classes
|
||||
#define BQ27441_ID_CODES 112 // Codes
|
||||
|
||||
/////////////////////////////////////////
|
||||
// OpConfig Register - Bit Definitions //
|
||||
/////////////////////////////////////////
|
||||
// Bit positions of the OpConfig Register
|
||||
#define BQ27441_OPCONFIG_BIE (1<<13)
|
||||
#define BQ27441_OPCONFIG_BI_PU_EN (1<<12)
|
||||
#define BQ27441_OPCONFIG_GPIOPOL (1<<11)
|
||||
#define BQ27441_OPCONFIG_SLEEP (1<<5)
|
||||
#define BQ27441_OPCONFIG_RMFCC (1<<4)
|
||||
#define BQ27441_OPCONFIG_BATLOWEN (1<<2)
|
||||
#define BQ27441_OPCONFIG_TEMPS (1<<0)
|
||||
739
main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp
Normal file
739
main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp
Normal file
@ -0,0 +1,739 @@
|
||||
// Modified or adapted from https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
//
|
||||
// Originally Licensed under the MIT
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 SparkFun Electronics, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/******************************************************************************
|
||||
SparkFunBQ27441.cpp
|
||||
BQ27441 Arduino Library Main Source File
|
||||
Jim Lindblom @ SparkFun Electronics
|
||||
May 9, 2016
|
||||
https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
|
||||
Implementation of all features of the BQ27441 LiPo Fuel Gauge.
|
||||
|
||||
Hardware Resources:
|
||||
- Arduino Development Board
|
||||
- SparkFun Battery Babysitter
|
||||
|
||||
Development environment specifics:
|
||||
Arduino 1.6.7
|
||||
SparkFun Battery Babysitter v1.0
|
||||
Arduino Uno (any 'duino should do)
|
||||
******************************************************************************/
|
||||
|
||||
#include "SparkFunBQ27441.h"
|
||||
#include "../i2c.h"
|
||||
|
||||
/*****************************************************************************
|
||||
************************** Initialization Functions *************************
|
||||
*****************************************************************************/
|
||||
// Initializes class variables
|
||||
BQ27441::BQ27441() : _deviceAddress(BQ72441_I2C_ADDRESS), _sealFlag(false), _userConfigControl(false)
|
||||
{
|
||||
}
|
||||
|
||||
static uint8_t constrain(uint8_t x, uint8_t min, uint8_t max) {
|
||||
if (x < min) return min;
|
||||
if (x > max) return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Initializes I2C and verifies communication with the BQ27441.
|
||||
bool BQ27441::begin(void)
|
||||
{
|
||||
uint16_t deviceID = 0;
|
||||
|
||||
// Assume the I2C is already initialized from other things
|
||||
|
||||
deviceID = deviceType(); // Read deviceType from BQ27441
|
||||
|
||||
if (deviceID == BQ27441_DEVICE_ID)
|
||||
{
|
||||
return true; // If device ID is valid, return true
|
||||
}
|
||||
|
||||
return false; // Otherwise return false
|
||||
}
|
||||
|
||||
// Configures the design capacity of the connected battery.
|
||||
bool BQ27441::setCapacity(uint16_t capacity)
|
||||
{
|
||||
// Write to STATE subclass (82) of BQ27441 extended memory.
|
||||
// Offset 0x0A (10)
|
||||
// Design capacity is a 2-byte piece of data - MSB first
|
||||
// Unit: mAh
|
||||
uint8_t capMSB = capacity >> 8;
|
||||
uint8_t capLSB = capacity & 0x00FF;
|
||||
uint8_t capacityData[2] = {capMSB, capLSB};
|
||||
return writeExtendedData(BQ27441_ID_STATE, 10, capacityData, 2);
|
||||
}
|
||||
|
||||
// Configures the design energy of the connected battery.
|
||||
bool BQ27441::setDesignEnergy(uint16_t energy)
|
||||
{
|
||||
// Write to STATE subclass (82) of BQ27441 extended memory.
|
||||
// Offset 0x0C (12)
|
||||
// Design energy is a 2-byte piece of data - MSB first
|
||||
// Unit: mWh
|
||||
uint8_t enMSB = energy >> 8;
|
||||
uint8_t enLSB = energy & 0x00FF;
|
||||
uint8_t energyData[2] = {enMSB, enLSB};
|
||||
return writeExtendedData(BQ27441_ID_STATE, 12, energyData, 2);
|
||||
}
|
||||
|
||||
// Configures the terminate voltage.
|
||||
bool BQ27441::setTerminateVoltage(uint16_t voltage)
|
||||
{
|
||||
// Write to STATE subclass (82) of BQ27441 extended memory.
|
||||
// Offset 0x0F (16)
|
||||
// Termiante voltage is a 2-byte piece of data - MSB first
|
||||
// Unit: mV
|
||||
// Min 2500, Max 3700
|
||||
if(voltage<2500) voltage=2500;
|
||||
if(voltage>3700) voltage=3700;
|
||||
|
||||
uint8_t tvMSB = voltage >> 8;
|
||||
uint8_t tvLSB = voltage & 0x00FF;
|
||||
uint8_t tvData[2] = {tvMSB, tvLSB};
|
||||
return writeExtendedData(BQ27441_ID_STATE, 16, tvData, 2);
|
||||
}
|
||||
|
||||
// Configures taper rate of connected battery.
|
||||
bool BQ27441::setTaperRate(uint16_t rate)
|
||||
{
|
||||
// Write to STATE subclass (82) of BQ27441 extended memory.
|
||||
// Offset 0x1B (27)
|
||||
// Termiante voltage is a 2-byte piece of data - MSB first
|
||||
// Unit: 0.1h
|
||||
// Max 2000
|
||||
if(rate>2000) rate=2000;
|
||||
uint8_t trMSB = rate >> 8;
|
||||
uint8_t trLSB = rate & 0x00FF;
|
||||
uint8_t trData[2] = {trMSB, trLSB};
|
||||
return writeExtendedData(BQ27441_ID_STATE, 27, trData, 2);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
********************** Battery Characteristics Functions ********************
|
||||
*****************************************************************************/
|
||||
|
||||
// Reads and returns the battery voltage
|
||||
uint16_t BQ27441::voltage(void)
|
||||
{
|
||||
return readWord(BQ27441_COMMAND_VOLTAGE);
|
||||
}
|
||||
|
||||
// Reads and returns the specified current measurement
|
||||
int16_t BQ27441::current(current_measure type)
|
||||
{
|
||||
int16_t current = 0;
|
||||
switch (type)
|
||||
{
|
||||
case AVG:
|
||||
current = (int16_t) readWord(BQ27441_COMMAND_AVG_CURRENT);
|
||||
break;
|
||||
case STBY:
|
||||
current = (int16_t) readWord(BQ27441_COMMAND_STDBY_CURRENT);
|
||||
break;
|
||||
case MAX:
|
||||
current = (int16_t) readWord(BQ27441_COMMAND_MAX_CURRENT);
|
||||
break;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
// Reads and returns the specified capacity measurement
|
||||
uint16_t BQ27441::capacity(capacity_measure type)
|
||||
{
|
||||
uint16_t capacity = 0;
|
||||
switch (type)
|
||||
{
|
||||
case REMAIN:
|
||||
return readWord(BQ27441_COMMAND_REM_CAPACITY);
|
||||
break;
|
||||
case FULL:
|
||||
return readWord(BQ27441_COMMAND_FULL_CAPACITY);
|
||||
break;
|
||||
case AVAIL:
|
||||
capacity = readWord(BQ27441_COMMAND_NOM_CAPACITY);
|
||||
break;
|
||||
case AVAIL_FULL:
|
||||
capacity = readWord(BQ27441_COMMAND_AVAIL_CAPACITY);
|
||||
break;
|
||||
case REMAIN_F:
|
||||
capacity = readWord(BQ27441_COMMAND_REM_CAP_FIL);
|
||||
break;
|
||||
case REMAIN_UF:
|
||||
capacity = readWord(BQ27441_COMMAND_REM_CAP_UNFL);
|
||||
break;
|
||||
case FULL_F:
|
||||
capacity = readWord(BQ27441_COMMAND_FULL_CAP_FIL);
|
||||
break;
|
||||
case FULL_UF:
|
||||
capacity = readWord(BQ27441_COMMAND_FULL_CAP_UNFL);
|
||||
break;
|
||||
case DESIGN:
|
||||
capacity = readWord(BQ27441_EXTENDED_CAPACITY);
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
// Reads and returns measured average power
|
||||
int16_t BQ27441::power(void)
|
||||
{
|
||||
return (int16_t) readWord(BQ27441_COMMAND_AVG_POWER);
|
||||
}
|
||||
|
||||
// Reads and returns specified state of charge measurement
|
||||
uint16_t BQ27441::soc(soc_measure type)
|
||||
{
|
||||
uint16_t socRet = 0;
|
||||
switch (type)
|
||||
{
|
||||
case FILTERED:
|
||||
socRet = readWord(BQ27441_COMMAND_SOC);
|
||||
break;
|
||||
case UNFILTERED:
|
||||
socRet = readWord(BQ27441_COMMAND_SOC_UNFL);
|
||||
break;
|
||||
}
|
||||
|
||||
return socRet;
|
||||
}
|
||||
|
||||
// Reads and returns specified state of health measurement
|
||||
uint8_t BQ27441::soh(soh_measure type)
|
||||
{
|
||||
uint16_t sohRaw = readWord(BQ27441_COMMAND_SOH);
|
||||
uint8_t sohStatus = sohRaw >> 8;
|
||||
uint8_t sohPercent = sohRaw & 0x00FF;
|
||||
|
||||
if (type == PERCENT)
|
||||
return sohPercent;
|
||||
else
|
||||
return sohStatus;
|
||||
}
|
||||
|
||||
// Reads and returns specified temperature measurement
|
||||
uint16_t BQ27441::temperature(temp_measure type)
|
||||
{
|
||||
uint16_t temp = 0;
|
||||
switch (type)
|
||||
{
|
||||
case BATTERY:
|
||||
temp = readWord(BQ27441_COMMAND_TEMP);
|
||||
break;
|
||||
case INTERNAL_TEMP:
|
||||
temp = readWord(BQ27441_COMMAND_INT_TEMP);
|
||||
break;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
************************** GPOUT Control Functions **************************
|
||||
*****************************************************************************/
|
||||
// Get GPOUT polarity setting (active-high or active-low)
|
||||
bool BQ27441::GPOUTPolarity(void)
|
||||
{
|
||||
uint16_t opConfigRegister = opConfig();
|
||||
|
||||
return (opConfigRegister & BQ27441_OPCONFIG_GPIOPOL);
|
||||
}
|
||||
|
||||
// Set GPOUT polarity to active-high or active-low
|
||||
bool BQ27441::setGPOUTPolarity(bool activeHigh)
|
||||
{
|
||||
uint16_t oldOpConfig = opConfig();
|
||||
|
||||
// Check to see if we need to update opConfig:
|
||||
if ((activeHigh && (oldOpConfig & BQ27441_OPCONFIG_GPIOPOL)) ||
|
||||
(!activeHigh && !(oldOpConfig & BQ27441_OPCONFIG_GPIOPOL)))
|
||||
return true;
|
||||
|
||||
uint16_t newOpConfig = oldOpConfig;
|
||||
if (activeHigh)
|
||||
newOpConfig |= BQ27441_OPCONFIG_GPIOPOL;
|
||||
else
|
||||
newOpConfig &= ~(BQ27441_OPCONFIG_GPIOPOL);
|
||||
|
||||
return writeOpConfig(newOpConfig);
|
||||
}
|
||||
|
||||
// Get GPOUT function (BAT_LOW or SOC_INT)
|
||||
bool BQ27441::GPOUTFunction(void)
|
||||
{
|
||||
uint16_t opConfigRegister = opConfig();
|
||||
|
||||
return (opConfigRegister & BQ27441_OPCONFIG_BATLOWEN);
|
||||
}
|
||||
|
||||
// Set GPOUT function to BAT_LOW or SOC_INT
|
||||
bool BQ27441::setGPOUTFunction(gpout_function function)
|
||||
{
|
||||
uint16_t oldOpConfig = opConfig();
|
||||
|
||||
// Check to see if we need to update opConfig:
|
||||
if ((function && (oldOpConfig & BQ27441_OPCONFIG_BATLOWEN)) ||
|
||||
(!function && !(oldOpConfig & BQ27441_OPCONFIG_BATLOWEN)))
|
||||
return true;
|
||||
|
||||
// Modify BATLOWN_EN bit of opConfig:
|
||||
uint16_t newOpConfig = oldOpConfig;
|
||||
if (function)
|
||||
newOpConfig |= BQ27441_OPCONFIG_BATLOWEN;
|
||||
else
|
||||
newOpConfig &= ~(BQ27441_OPCONFIG_BATLOWEN);
|
||||
|
||||
// Write new opConfig
|
||||
return writeOpConfig(newOpConfig);
|
||||
}
|
||||
|
||||
// Get SOC1_Set Threshold - threshold to set the alert flag
|
||||
uint8_t BQ27441::SOC1SetThreshold(void)
|
||||
{
|
||||
return readExtendedData(BQ27441_ID_DISCHARGE, 0);
|
||||
}
|
||||
|
||||
// Get SOC1_Clear Threshold - threshold to clear the alert flag
|
||||
uint8_t BQ27441::SOC1ClearThreshold(void)
|
||||
{
|
||||
return readExtendedData(BQ27441_ID_DISCHARGE, 1);
|
||||
}
|
||||
|
||||
// Set the SOC1 set and clear thresholds to a percentage
|
||||
bool BQ27441::setSOC1Thresholds(uint8_t set, uint8_t clear)
|
||||
{
|
||||
uint8_t thresholds[2];
|
||||
thresholds[0] = constrain(set, 0, 100);
|
||||
thresholds[1] = constrain(clear, 0, 100);
|
||||
return writeExtendedData(BQ27441_ID_DISCHARGE, 0, thresholds, 2);
|
||||
}
|
||||
|
||||
// Get SOCF_Set Threshold - threshold to set the alert flag
|
||||
uint8_t BQ27441::SOCFSetThreshold(void)
|
||||
{
|
||||
return readExtendedData(BQ27441_ID_DISCHARGE, 2);
|
||||
}
|
||||
|
||||
// Get SOCF_Clear Threshold - threshold to clear the alert flag
|
||||
uint8_t BQ27441::SOCFClearThreshold(void)
|
||||
{
|
||||
return readExtendedData(BQ27441_ID_DISCHARGE, 3);
|
||||
}
|
||||
|
||||
// Set the SOCF set and clear thresholds to a percentage
|
||||
bool BQ27441::setSOCFThresholds(uint8_t set, uint8_t clear)
|
||||
{
|
||||
uint8_t thresholds[2];
|
||||
thresholds[0] = constrain(set, 0, 100);
|
||||
thresholds[1] = constrain(clear, 0, 100);
|
||||
return writeExtendedData(BQ27441_ID_DISCHARGE, 2, thresholds, 2);
|
||||
}
|
||||
|
||||
// Check if the SOC1 flag is set
|
||||
bool BQ27441::socFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_SOC1;
|
||||
}
|
||||
|
||||
// Check if the SOCF flag is set
|
||||
bool BQ27441::socfFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_SOCF;
|
||||
|
||||
}
|
||||
|
||||
// Check if the ITPOR flag is set
|
||||
bool BQ27441::itporFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_ITPOR;
|
||||
}
|
||||
|
||||
// Check if the FC flag is set
|
||||
bool BQ27441::fcFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_FC;
|
||||
}
|
||||
|
||||
// Check if the CHG flag is set
|
||||
bool BQ27441::chgFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_CHG;
|
||||
}
|
||||
|
||||
// Check if the DSG flag is set
|
||||
bool BQ27441::dsgFlag(void)
|
||||
{
|
||||
uint16_t flagState = flags();
|
||||
|
||||
return flagState & BQ27441_FLAG_DSG;
|
||||
}
|
||||
|
||||
// Get the SOC_INT interval delta
|
||||
uint8_t BQ27441::sociDelta(void)
|
||||
{
|
||||
return readExtendedData(BQ27441_ID_STATE, 26);
|
||||
}
|
||||
|
||||
// Set the SOC_INT interval delta to a value between 1 and 100
|
||||
bool BQ27441::setSOCIDelta(uint8_t delta)
|
||||
{
|
||||
uint8_t soci = constrain(delta, 0, 100);
|
||||
return writeExtendedData(BQ27441_ID_STATE, 26, &soci, 1);
|
||||
}
|
||||
|
||||
// Pulse the GPOUT pin - must be in SOC_INT mode
|
||||
bool BQ27441::pulseGPOUT(void)
|
||||
{
|
||||
return executeControlWord(BQ27441_CONTROL_PULSE_SOC_INT);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*************************** Control Sub-Commands ****************************
|
||||
*****************************************************************************/
|
||||
|
||||
// Read the device type - should be 0x0421
|
||||
uint16_t BQ27441::deviceType(void)
|
||||
{
|
||||
return readControlWord(BQ27441_CONTROL_DEVICE_TYPE);
|
||||
}
|
||||
|
||||
// Enter configuration mode - set userControl if calling from an Arduino sketch
|
||||
// and you want control over when to exitConfig
|
||||
bool BQ27441::enterConfig(bool userControl)
|
||||
{
|
||||
if (userControl) _userConfigControl = true;
|
||||
|
||||
if (sealed())
|
||||
{
|
||||
_sealFlag = true;
|
||||
unseal(); // Must be unsealed before making changes
|
||||
}
|
||||
|
||||
if (executeControlWord(BQ27441_CONTROL_SET_CFGUPDATE))
|
||||
{
|
||||
int16_t timeout = BQ72441_I2C_TIMEOUT;
|
||||
while ((timeout--) && (!(flags() & BQ27441_FLAG_CFGUPMODE)))
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
|
||||
if (timeout > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exit configuration mode with the option to perform a resimulation
|
||||
bool BQ27441::exitConfig(bool resim)
|
||||
{
|
||||
// There are two methods for exiting config mode:
|
||||
// 1. Execute the EXIT_CFGUPDATE command
|
||||
// 2. Execute the SOFT_RESET command
|
||||
// EXIT_CFGUPDATE exits config mode _without_ an OCV (open-circuit voltage)
|
||||
// measurement, and without resimulating to update unfiltered-SoC and SoC.
|
||||
// If a new OCV measurement or resimulation is desired, SOFT_RESET or
|
||||
// EXIT_RESIM should be used to exit config mode.
|
||||
if (resim)
|
||||
{
|
||||
if (softReset())
|
||||
{
|
||||
int16_t timeout = BQ72441_I2C_TIMEOUT;
|
||||
while ((timeout--) && ((flags() & BQ27441_FLAG_CFGUPMODE)))
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
if (timeout > 0)
|
||||
{
|
||||
if (_sealFlag) seal(); // Seal back up if we IC was sealed coming in
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return executeControlWord(BQ27441_CONTROL_EXIT_CFGUPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the flags() command
|
||||
uint16_t BQ27441::flags(void)
|
||||
{
|
||||
return readWord(BQ27441_COMMAND_FLAGS);
|
||||
}
|
||||
|
||||
// Read the CONTROL_STATUS subcommand of control()
|
||||
uint16_t BQ27441::status(void)
|
||||
{
|
||||
return readControlWord(BQ27441_CONTROL_STATUS);
|
||||
}
|
||||
|
||||
/***************************** Private Functions *****************************/
|
||||
|
||||
// Check if the BQ27441-G1A is sealed or not.
|
||||
bool BQ27441::sealed(void)
|
||||
{
|
||||
uint16_t stat = status();
|
||||
return stat & BQ27441_STATUS_SS;
|
||||
}
|
||||
|
||||
// Seal the BQ27441-G1A
|
||||
bool BQ27441::seal(void)
|
||||
{
|
||||
return readControlWord(BQ27441_CONTROL_SEALED);
|
||||
}
|
||||
|
||||
// UNseal the BQ27441-G1A
|
||||
bool BQ27441::unseal(void)
|
||||
{
|
||||
// To unseal the BQ27441, write the key to the control
|
||||
// command. Then immediately write the same key to control again.
|
||||
if (readControlWord(BQ27441_UNSEAL_KEY))
|
||||
{
|
||||
return readControlWord(BQ27441_UNSEAL_KEY);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the 16-bit opConfig register from extended data
|
||||
uint16_t BQ27441::opConfig(void)
|
||||
{
|
||||
return readWord(BQ27441_EXTENDED_OPCONFIG);
|
||||
}
|
||||
|
||||
// Write the 16-bit opConfig register in extended data
|
||||
bool BQ27441::writeOpConfig(uint16_t value)
|
||||
{
|
||||
uint8_t opConfigMSB = value >> 8;
|
||||
uint8_t opConfigLSB = value & 0x00FF;
|
||||
uint8_t opConfigData[2] = {opConfigMSB, opConfigLSB};
|
||||
|
||||
// OpConfig register location: BQ27441_ID_REGISTERS id, offset 0
|
||||
return writeExtendedData(BQ27441_ID_REGISTERS, 0, opConfigData, 2);
|
||||
}
|
||||
|
||||
// Issue a soft-reset to the BQ27441-G1A
|
||||
bool BQ27441::softReset(void)
|
||||
{
|
||||
return executeControlWord(BQ27441_CONTROL_SOFT_RESET);
|
||||
}
|
||||
|
||||
// Read a 16-bit command word from the BQ27441-G1A
|
||||
uint16_t BQ27441::readWord(uint16_t subAddress)
|
||||
{
|
||||
uint8_t data[2];
|
||||
i2cReadBytes(subAddress, data, 2);
|
||||
return ((uint16_t) data[1] << 8) | data[0];
|
||||
}
|
||||
|
||||
// Read a 16-bit subcommand() from the BQ27441-G1A's control()
|
||||
uint16_t BQ27441::readControlWord(uint16_t function)
|
||||
{
|
||||
uint8_t subCommandMSB = (function >> 8);
|
||||
uint8_t subCommandLSB = (function & 0x00FF);
|
||||
uint8_t command[3] = {0, subCommandLSB, subCommandMSB};
|
||||
uint8_t data[2] = {0, 0};
|
||||
|
||||
i2cWriteBytes((uint8_t) 0, command, 2);
|
||||
|
||||
if (i2cReadBytes((uint8_t) 0, data, 2))
|
||||
{
|
||||
return ((uint16_t)data[1] << 8) | data[0];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute a subcommand() from the BQ27441-G1A's control()
|
||||
bool BQ27441::executeControlWord(uint16_t function)
|
||||
{
|
||||
uint8_t subCommandMSB = (function >> 8);
|
||||
uint8_t subCommandLSB = (function & 0x00FF);
|
||||
uint8_t command[2] = {subCommandLSB, subCommandMSB};
|
||||
|
||||
if (i2cWriteBytes((uint8_t) 0, command, 2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
************************** Extended Data Commands ***************************
|
||||
*****************************************************************************/
|
||||
|
||||
// Issue a BlockDataControl() command to enable BlockData access
|
||||
bool BQ27441::blockDataControl(void)
|
||||
{
|
||||
uint8_t enableByte = 0x00;
|
||||
return i2cWriteBytes(BQ27441_EXTENDED_CONTROL, &enableByte, 1);
|
||||
}
|
||||
|
||||
// Issue a DataClass() command to set the data class to be accessed
|
||||
bool BQ27441::blockDataClass(uint8_t id)
|
||||
{
|
||||
return i2cWriteBytes(BQ27441_EXTENDED_DATACLASS, &id, 1);
|
||||
}
|
||||
|
||||
// Issue a DataBlock() command to set the data block to be accessed
|
||||
bool BQ27441::blockDataOffset(uint8_t offset)
|
||||
{
|
||||
return i2cWriteBytes(BQ27441_EXTENDED_DATABLOCK, &offset, 1);
|
||||
}
|
||||
|
||||
// Read the current checksum using BlockDataCheckSum()
|
||||
uint8_t BQ27441::blockDataChecksum(void)
|
||||
{
|
||||
uint8_t csum;
|
||||
i2cReadBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1);
|
||||
return csum;
|
||||
}
|
||||
|
||||
// Use BlockData() to read a byte from the loaded extended data
|
||||
uint8_t BQ27441::readBlockData(uint8_t offset)
|
||||
{
|
||||
uint8_t ret;
|
||||
uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA;
|
||||
i2cReadBytes(address, &ret, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Use BlockData() to write a byte to an offset of the loaded data
|
||||
bool BQ27441::writeBlockData(uint8_t offset, uint8_t data)
|
||||
{
|
||||
uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA;
|
||||
return i2cWriteBytes(address, &data, 1);
|
||||
}
|
||||
|
||||
// Read all 32 bytes of the loaded extended data and compute a
|
||||
// checksum based on the values.
|
||||
uint8_t BQ27441::computeBlockChecksum(void)
|
||||
{
|
||||
uint8_t data[32];
|
||||
i2cReadBytes(BQ27441_EXTENDED_BLOCKDATA, data, 32);
|
||||
|
||||
uint8_t csum = 0;
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
csum += data[i];
|
||||
}
|
||||
csum = 255 - csum;
|
||||
|
||||
return csum;
|
||||
}
|
||||
|
||||
// Use the BlockDataCheckSum() command to write a checksum value
|
||||
bool BQ27441::writeBlockChecksum(uint8_t csum)
|
||||
{
|
||||
return i2cWriteBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1);
|
||||
}
|
||||
|
||||
// Read a byte from extended data specifying a class ID and position offset
|
||||
uint8_t BQ27441::readExtendedData(uint8_t classID, uint8_t offset)
|
||||
{
|
||||
uint8_t retData = 0;
|
||||
if (!_userConfigControl) enterConfig(false);
|
||||
|
||||
if (!blockDataControl()) // // enable block data memory control
|
||||
return false; // Return false if enable fails
|
||||
if (!blockDataClass(classID)) // Write class ID using DataBlockClass()
|
||||
return false;
|
||||
|
||||
blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0)
|
||||
|
||||
computeBlockChecksum(); // Compute checksum going in
|
||||
retData = readBlockData(offset % 32); // Read from offset (limit to 0-31)
|
||||
|
||||
if (!_userConfigControl) exitConfig();
|
||||
|
||||
return retData;
|
||||
}
|
||||
|
||||
// Write a specified number of bytes to extended data specifying a
|
||||
// class ID, position offset.
|
||||
bool BQ27441::writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len)
|
||||
{
|
||||
if (len > 32)
|
||||
return false;
|
||||
|
||||
if (!_userConfigControl) enterConfig(false);
|
||||
|
||||
if (!blockDataControl()) // // enable block data memory control
|
||||
return false; // Return false if enable fails
|
||||
if (!blockDataClass(classID)) // Write class ID using DataBlockClass()
|
||||
return false;
|
||||
|
||||
blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0)
|
||||
computeBlockChecksum(); // Compute checksum going in
|
||||
|
||||
// Write data bytes:
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
// Write to offset, mod 32 if offset is greater than 32
|
||||
// The blockDataOffset above sets the 32-bit block
|
||||
writeBlockData((offset % 32) + i, data[i]);
|
||||
}
|
||||
|
||||
// Write new checksum using BlockDataChecksum (0x60)
|
||||
uint8_t newCsum = computeBlockChecksum(); // Compute the new checksum
|
||||
writeBlockChecksum(newCsum);
|
||||
|
||||
if (!_userConfigControl) exitConfig();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
************************ I2C Read and Write Routines ************************
|
||||
*****************************************************************************/
|
||||
|
||||
// Read a specified number of bytes over I2C at a given subAddress
|
||||
int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
|
||||
{
|
||||
int16_t timeout = BQ72441_I2C_TIMEOUT;
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
i2c_master_write_read_device(BQ72441_I2C_NUM, _deviceAddress, &subAddress, 1, dest, count, timeout);
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
// Write a specified number of bytes over I2C to a given subAddress
|
||||
uint16_t BQ27441::i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count)
|
||||
{
|
||||
int16_t timeout = BQ72441_I2C_TIMEOUT;
|
||||
|
||||
// prepend the subAddress to the data.
|
||||
uint8_t w_buff[count+1] = {0};
|
||||
w_buff[0] = subAddress;
|
||||
for (int i = 0; i < count; i ++) {
|
||||
w_buff[i+1] = src[i];
|
||||
}
|
||||
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
esp_err_t ret = i2c_master_write_to_device(BQ72441_I2C_NUM, _deviceAddress, src, count+1, timeout);
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
|
||||
return ret == ESP_OK;
|
||||
}
|
||||
|
||||
BQ27441 lipo;
|
||||
564
main/drivers/SparkFunBQ27441/SparkFunBQ27441.h
Normal file
564
main/drivers/SparkFunBQ27441/SparkFunBQ27441.h
Normal file
@ -0,0 +1,564 @@
|
||||
// Modified or adapted from https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
//
|
||||
// Originally Licensed under the MIT
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 SparkFun Electronics, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/******************************************************************************
|
||||
SparkFunBQ27441.h
|
||||
BQ27441 Arduino Library Main Header File
|
||||
Jim Lindblom @ SparkFun Electronics
|
||||
May 9, 2016
|
||||
https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
|
||||
|
||||
Definition of the BQ27441 library, which implements all features of the
|
||||
BQ27441 LiPo Fuel Gauge.
|
||||
|
||||
Hardware Resources:
|
||||
- Arduino Development Board
|
||||
- SparkFun Battery Babysitter
|
||||
|
||||
Development environment specifics:
|
||||
Arduino 1.6.7
|
||||
SparkFun Battery Babysitter v1.0
|
||||
Arduino Uno (any 'duino should do)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef SparkFunBQ27441_h
|
||||
#define SparkFunBQ27441_h
|
||||
|
||||
#include "BQ27441_Definitions.h"
|
||||
#include <stdint.h>
|
||||
#include "driver/i2c_master.h"
|
||||
|
||||
#define BQ72441_I2C_NUM I2C_NUM_0
|
||||
|
||||
#define BQ72441_I2C_TIMEOUT 2000
|
||||
|
||||
// Parameters for the current() function, to specify which current to read
|
||||
typedef enum {
|
||||
AVG, // Average Current (DEFAULT)
|
||||
STBY, // Standby Current
|
||||
MAX // Max Current
|
||||
} current_measure;
|
||||
|
||||
// Parameters for the capacity() function, to specify which capacity to read
|
||||
typedef enum {
|
||||
REMAIN, // Remaining Capacity (DEFAULT)
|
||||
FULL, // Full Capacity
|
||||
AVAIL, // Available Capacity
|
||||
AVAIL_FULL, // Full Available Capacity
|
||||
REMAIN_F, // Remaining Capacity Filtered
|
||||
REMAIN_UF, // Remaining Capacity Unfiltered
|
||||
FULL_F, // Full Capacity Filtered
|
||||
FULL_UF, // Full Capacity Unfiltered
|
||||
DESIGN // Design Capacity
|
||||
} capacity_measure;
|
||||
|
||||
// Parameters for the soc() function
|
||||
typedef enum {
|
||||
FILTERED, // State of Charge Filtered (DEFAULT)
|
||||
UNFILTERED // State of Charge Unfiltered
|
||||
} soc_measure;
|
||||
|
||||
// Parameters for the soh() function
|
||||
typedef enum {
|
||||
PERCENT, // State of Health Percentage (DEFAULT)
|
||||
SOH_STAT // State of Health Status Bits
|
||||
} soh_measure;
|
||||
|
||||
// Parameters for the temperature() function
|
||||
typedef enum {
|
||||
BATTERY, // Battery Temperature (DEFAULT)
|
||||
INTERNAL_TEMP // Internal IC Temperature
|
||||
} temp_measure;
|
||||
|
||||
// Parameters for the setGPOUTFunction() funciton
|
||||
typedef enum {
|
||||
SOC_INT, // Set GPOUT to SOC_INT functionality
|
||||
BAT_LOW // Set GPOUT to BAT_LOW functionality
|
||||
} gpout_function;
|
||||
|
||||
class BQ27441 {
|
||||
public:
|
||||
//////////////////////////////
|
||||
// Initialization Functions //
|
||||
//////////////////////////////
|
||||
/**
|
||||
Initializes class variables
|
||||
*/
|
||||
BQ27441();
|
||||
|
||||
/**
|
||||
Initializes I2C and verifies communication with the BQ27441.
|
||||
Must be called before using any other functions.
|
||||
|
||||
@return true if communication was successful.
|
||||
*/
|
||||
bool begin(void);
|
||||
|
||||
/**
|
||||
Configures the design capacity of the connected battery.
|
||||
|
||||
@param capacity of battery (unsigned 16-bit value)
|
||||
@return true if capacity successfully set.
|
||||
*/
|
||||
bool setCapacity(uint16_t capacity);
|
||||
|
||||
/**
|
||||
Configures the design energy of the connected battery.
|
||||
|
||||
@param energy of battery (unsigned 16-bit value)
|
||||
@return true if energy successfully set.
|
||||
*/
|
||||
bool setDesignEnergy(uint16_t energy);
|
||||
|
||||
/**
|
||||
Configures terminate voltage (lowest operational voltage of battery powered circuit)
|
||||
|
||||
@param voltage of battery (unsigned 16-bit value)
|
||||
@return true if energy successfully set.
|
||||
*/
|
||||
bool setTerminateVoltage(uint16_t voltage);
|
||||
|
||||
bool setTaperRate(uint16_t rate);
|
||||
|
||||
/////////////////////////////
|
||||
// Battery Characteristics //
|
||||
/////////////////////////////
|
||||
/**
|
||||
Reads and returns the battery voltage
|
||||
|
||||
@return battery voltage in mV
|
||||
*/
|
||||
uint16_t voltage(void);
|
||||
|
||||
/**
|
||||
Reads and returns the specified current measurement
|
||||
|
||||
@param current_measure enum specifying current value to be read
|
||||
@return specified current measurement in mA. >0 indicates charging.
|
||||
*/
|
||||
int16_t current(current_measure type = AVG);
|
||||
|
||||
/**
|
||||
Reads and returns the specified capacity measurement
|
||||
|
||||
@param capacity_measure enum specifying capacity value to be read
|
||||
@return specified capacity measurement in mAh.
|
||||
*/
|
||||
uint16_t capacity(capacity_measure type = REMAIN);
|
||||
|
||||
/**
|
||||
Reads and returns measured average power
|
||||
|
||||
@return average power in mAh. >0 indicates charging.
|
||||
*/
|
||||
int16_t power(void);
|
||||
|
||||
/**
|
||||
Reads and returns specified state of charge measurement
|
||||
|
||||
@param soc_measure enum specifying filtered or unfiltered measurement
|
||||
@return specified state of charge measurement in %
|
||||
*/
|
||||
uint16_t soc(soc_measure type = FILTERED);
|
||||
|
||||
/**
|
||||
Reads and returns specified state of health measurement
|
||||
|
||||
@param soh_measure enum specifying filtered or unfiltered measurement
|
||||
@return specified state of health measurement in %, or status bits
|
||||
*/
|
||||
uint8_t soh(soh_measure type = PERCENT);
|
||||
|
||||
/**
|
||||
Reads and returns specified temperature measurement
|
||||
|
||||
@param temp_measure enum specifying internal or battery measurement
|
||||
@return specified temperature measurement in degrees C
|
||||
*/
|
||||
uint16_t temperature(temp_measure type = BATTERY);
|
||||
|
||||
////////////////////////////
|
||||
// GPOUT Control Commands //
|
||||
////////////////////////////
|
||||
/**
|
||||
Get GPOUT polarity setting (active-high or active-low)
|
||||
|
||||
@return true if active-high, false if active-low
|
||||
*/
|
||||
bool GPOUTPolarity(void);
|
||||
|
||||
/**
|
||||
Set GPOUT polarity to active-high or active-low
|
||||
|
||||
@param activeHigh is true if active-high, false if active-low
|
||||
@return true on success
|
||||
*/
|
||||
bool setGPOUTPolarity(bool activeHigh);
|
||||
|
||||
/**
|
||||
Get GPOUT function (BAT_LOW or SOC_INT)
|
||||
|
||||
@return true if BAT_LOW or false if SOC_INT
|
||||
*/
|
||||
bool GPOUTFunction(void);
|
||||
|
||||
/**
|
||||
Set GPOUT function to BAT_LOW or SOC_INT
|
||||
|
||||
@param function should be either BAT_LOW or SOC_INT
|
||||
@return true on success
|
||||
*/
|
||||
bool setGPOUTFunction(gpout_function function);
|
||||
|
||||
/**
|
||||
Get SOC1_Set Threshold - threshold to set the alert flag
|
||||
|
||||
@return state of charge value between 0 and 100%
|
||||
*/
|
||||
uint8_t SOC1SetThreshold(void);
|
||||
|
||||
/**
|
||||
Get SOC1_Clear Threshold - threshold to clear the alert flag
|
||||
|
||||
@return state of charge value between 0 and 100%
|
||||
*/
|
||||
uint8_t SOC1ClearThreshold(void);
|
||||
|
||||
/**
|
||||
Set the SOC1 set and clear thresholds to a percentage
|
||||
|
||||
@param set and clear percentages between 0 and 100. clear > set.
|
||||
@return true on success
|
||||
*/
|
||||
bool setSOC1Thresholds(uint8_t set, uint8_t clear);
|
||||
|
||||
/**
|
||||
Get SOCF_Set Threshold - threshold to set the alert flag
|
||||
|
||||
@return state of charge value between 0 and 100%
|
||||
*/
|
||||
uint8_t SOCFSetThreshold(void);
|
||||
|
||||
/**
|
||||
Get SOCF_Clear Threshold - threshold to clear the alert flag
|
||||
|
||||
@return state of charge value between 0 and 100%
|
||||
*/
|
||||
uint8_t SOCFClearThreshold(void);
|
||||
|
||||
/**
|
||||
Set the SOCF set and clear thresholds to a percentage
|
||||
|
||||
@param set and clear percentages between 0 and 100. clear > set.
|
||||
@return true on success
|
||||
*/
|
||||
bool setSOCFThresholds(uint8_t set, uint8_t clear);
|
||||
|
||||
/**
|
||||
Check if the SOC1 flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool socFlag(void);
|
||||
|
||||
/**
|
||||
Check if the SOCF flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool socfFlag(void);
|
||||
|
||||
/**
|
||||
Check if the ITPOR flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool itporFlag(void);
|
||||
|
||||
/**
|
||||
Check if the FC flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool fcFlag(void);
|
||||
|
||||
/**
|
||||
Check if the CHG flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool chgFlag(void);
|
||||
|
||||
/**
|
||||
Check if the DSG flag is set in flags()
|
||||
|
||||
@return true if flag is set
|
||||
*/
|
||||
bool dsgFlag(void);
|
||||
|
||||
|
||||
/**
|
||||
Get the SOC_INT interval delta
|
||||
|
||||
@return interval percentage value between 1 and 100
|
||||
*/
|
||||
uint8_t sociDelta(void);
|
||||
|
||||
/**
|
||||
Set the SOC_INT interval delta to a value between 1 and 100
|
||||
|
||||
@param interval percentage value between 1 and 100
|
||||
@return true on success
|
||||
*/
|
||||
bool setSOCIDelta(uint8_t delta);
|
||||
|
||||
/**
|
||||
Pulse the GPOUT pin - must be in SOC_INT mode
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
bool pulseGPOUT(void);
|
||||
|
||||
//////////////////////////
|
||||
// Control Sub-commands //
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
Read the device type - should be 0x0421
|
||||
|
||||
@return 16-bit value read from DEVICE_TYPE subcommand
|
||||
*/
|
||||
uint16_t deviceType(void);
|
||||
|
||||
/**
|
||||
Enter configuration mode - set userControl if calling from an Arduino
|
||||
sketch and you want control over when to exitConfig.
|
||||
|
||||
@param userControl is true if the Arduino sketch is handling entering
|
||||
and exiting config mode (should be false in library calls).
|
||||
@return true on success
|
||||
*/
|
||||
bool enterConfig(bool userControl = true);
|
||||
|
||||
/**
|
||||
Exit configuration mode with the option to perform a resimulation
|
||||
|
||||
@param resim is true if resimulation should be performed after exiting
|
||||
@return true on success
|
||||
*/
|
||||
bool exitConfig(bool resim = true);
|
||||
|
||||
/**
|
||||
Read the flags() command
|
||||
|
||||
@return 16-bit representation of flags() command register
|
||||
*/
|
||||
uint16_t flags(void);
|
||||
|
||||
/**
|
||||
Read the CONTROL_STATUS subcommand of control()
|
||||
|
||||
@return 16-bit representation of CONTROL_STATUS subcommand
|
||||
*/
|
||||
uint16_t status(void);
|
||||
|
||||
private:
|
||||
uint8_t _deviceAddress; // Stores the BQ27441-G1A's I2C address
|
||||
bool _sealFlag; // Global to identify that IC was previously sealed
|
||||
bool _userConfigControl; // Global to identify that user has control over
|
||||
// entering/exiting config
|
||||
|
||||
/**
|
||||
Check if the BQ27441-G1A is sealed or not.
|
||||
|
||||
@return true if the chip is sealed
|
||||
*/
|
||||
bool sealed(void);
|
||||
|
||||
/**
|
||||
Seal the BQ27441-G1A
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
bool seal(void);
|
||||
|
||||
/**
|
||||
UNseal the BQ27441-G1A
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
bool unseal(void);
|
||||
|
||||
/**
|
||||
Read the 16-bit opConfig register from extended data
|
||||
|
||||
@return opConfig register contents
|
||||
*/
|
||||
uint16_t opConfig(void);
|
||||
|
||||
/**
|
||||
Write the 16-bit opConfig register in extended data
|
||||
|
||||
@param New 16-bit value for opConfig
|
||||
@return true on success
|
||||
*/
|
||||
bool writeOpConfig(uint16_t value);
|
||||
|
||||
/**
|
||||
Issue a soft-reset to the BQ27441-G1A
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
bool softReset(void);
|
||||
|
||||
/**
|
||||
Read a 16-bit command word from the BQ27441-G1A
|
||||
|
||||
@param subAddress is the command to be read from
|
||||
@return 16-bit value of the command's contents
|
||||
*/
|
||||
uint16_t readWord(uint16_t subAddress);
|
||||
|
||||
/**
|
||||
Read a 16-bit subcommand() from the BQ27441-G1A's control()
|
||||
|
||||
@param function is the subcommand of control() to be read
|
||||
@return 16-bit value of the subcommand's contents
|
||||
*/
|
||||
uint16_t readControlWord(uint16_t function);
|
||||
|
||||
/**
|
||||
Execute a subcommand() from the BQ27441-G1A's control()
|
||||
|
||||
@param function is the subcommand of control() to be executed
|
||||
@return true on success
|
||||
*/
|
||||
bool executeControlWord(uint16_t function);
|
||||
|
||||
////////////////////////////
|
||||
// Extended Data Commands //
|
||||
////////////////////////////
|
||||
/**
|
||||
Issue a BlockDataControl() command to enable BlockData access
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
bool blockDataControl(void);
|
||||
|
||||
/**
|
||||
Issue a DataClass() command to set the data class to be accessed
|
||||
|
||||
@param id is the id number of the class
|
||||
@return true on success
|
||||
*/
|
||||
bool blockDataClass(uint8_t id);
|
||||
|
||||
/**
|
||||
Issue a DataBlock() command to set the data block to be accessed
|
||||
|
||||
@param offset of the data block
|
||||
@return true on success
|
||||
*/
|
||||
bool blockDataOffset(uint8_t offset);
|
||||
|
||||
/**
|
||||
Read the current checksum using BlockDataCheckSum()
|
||||
|
||||
@return true on success
|
||||
*/
|
||||
uint8_t blockDataChecksum(void);
|
||||
|
||||
/**
|
||||
Use BlockData() to read a byte from the loaded extended data
|
||||
|
||||
@param offset of data block byte to be read
|
||||
@return true on success
|
||||
*/
|
||||
uint8_t readBlockData(uint8_t offset);
|
||||
|
||||
/**
|
||||
Use BlockData() to write a byte to an offset of the loaded data
|
||||
|
||||
@param offset is the position of the byte to be written
|
||||
data is the value to be written
|
||||
@return true on success
|
||||
*/
|
||||
bool writeBlockData(uint8_t offset, uint8_t data);
|
||||
|
||||
/**
|
||||
Read all 32 bytes of the loaded extended data and compute a
|
||||
checksum based on the values.
|
||||
|
||||
@return 8-bit checksum value calculated based on loaded data
|
||||
*/
|
||||
uint8_t computeBlockChecksum(void);
|
||||
|
||||
/**
|
||||
Use the BlockDataCheckSum() command to write a checksum value
|
||||
|
||||
@param csum is the 8-bit checksum to be written
|
||||
@return true on success
|
||||
*/
|
||||
bool writeBlockChecksum(uint8_t csum);
|
||||
|
||||
/**
|
||||
Read a byte from extended data specifying a class ID and position offset
|
||||
|
||||
@param classID is the id of the class to be read from
|
||||
offset is the byte position of the byte to be read
|
||||
@return 8-bit value of specified data
|
||||
*/
|
||||
uint8_t readExtendedData(uint8_t classID, uint8_t offset);
|
||||
|
||||
/**
|
||||
Write a specified number of bytes to extended data specifying a
|
||||
class ID, position offset.
|
||||
|
||||
@param classID is the id of the class to be read from
|
||||
offset is the byte position of the byte to be read
|
||||
data is the data buffer to be written
|
||||
len is the number of bytes to be written
|
||||
@return true on success
|
||||
*/
|
||||
bool writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len);
|
||||
|
||||
/////////////////////////////////
|
||||
// I2C Read and Write Routines //
|
||||
/////////////////////////////////
|
||||
|
||||
/**
|
||||
Read a specified number of bytes over I2C at a given subAddress
|
||||
|
||||
@param subAddress is the 8-bit address of the data to be read
|
||||
dest is the data buffer to be written to
|
||||
count is the number of bytes to be read
|
||||
@return true on success
|
||||
*/
|
||||
int16_t i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count);
|
||||
|
||||
/**
|
||||
Write a specified number of bytes over I2C to a given subAddress
|
||||
|
||||
@param subAddress is the 8-bit address of the data to be written to
|
||||
src is the data buffer to be written
|
||||
count is the number of bytes to be written
|
||||
@return true on success
|
||||
*/
|
||||
uint16_t i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count);
|
||||
};
|
||||
|
||||
extern BQ27441 lipo; // Use lipo.[] to interact with the library in an Arduino sketch
|
||||
// Thanks for reading!
|
||||
|
||||
#endif
|
||||
61
main/drivers/TM1640/TM1640.cpp
Normal file
61
main/drivers/TM1640/TM1640.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
TM1640.cpp - Library implementation for TM1640.
|
||||
|
||||
Copyright (C) 2011 Ricardo Batista (rjbatista <at> gmail <dot> com)
|
||||
Adjusted for TM1640 by Maxint R&D, based on orignal code. See https://github.com/maxint-rd/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the version 3 GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TM1640.h"
|
||||
#include <stdint.h>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
TM1640::TM1640(gpio_num_t dataPin, gpio_num_t clockPin, uint8_t numDigits, bool activateDisplay, uint8_t intensity)
|
||||
: TM16xx(dataPin, clockPin, dataPin, TM1640_MAX_POS, numDigits, activateDisplay, intensity)
|
||||
{ // NOTE: Like the TM1637, the TM1640 only has DIO and CLK. Therefor the DIO-pin is initialized as strobe in the constructor
|
||||
clearDisplay();
|
||||
setupDisplay(activateDisplay, intensity);
|
||||
}
|
||||
|
||||
/*
|
||||
void TM1640::bitDelay()
|
||||
{
|
||||
//delayMicroseconds(5);
|
||||
}
|
||||
*/
|
||||
|
||||
void TM1640::start()
|
||||
{ // if needed derived classes can use different patterns to start a command (eg. for TM1637)
|
||||
// Datasheet: The starting condition of data input is: when CLK is high, the DIN becomes low from high;
|
||||
gpio_set_level(dataPin, 0);
|
||||
gpio_set_level(clockPin, 0);
|
||||
bitDelay();
|
||||
}
|
||||
|
||||
void TM1640::stop()
|
||||
{ // if needed derived classes can use different patterns to stop a command (eg. for TM1637)
|
||||
// datasheet: the ending condition is: when CLK is high, the DIN becomes high from low.
|
||||
gpio_set_level(clockPin, 1);
|
||||
gpio_set_level(dataPin, 1);
|
||||
bitDelay();
|
||||
}
|
||||
|
||||
void TM1640::send(uint8_t data)
|
||||
{
|
||||
// MOLE 180514: TM1640 wants data and clock to be low after sending the data
|
||||
TM16xx::send(data);
|
||||
gpio_set_level(clockPin, 0); // first clock low
|
||||
gpio_set_level(dataPin, 0); // then data low
|
||||
bitDelay();
|
||||
}
|
||||
41
main/drivers/TM1640/TM1640.h
Normal file
41
main/drivers/TM1640/TM1640.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
TM1640.h - Library for TM1640.
|
||||
|
||||
Copyright (C) 2011 Ricardo Batista <rjbatista at gmail dot com>
|
||||
Adjusted for TM1640 by Maxint R&D, based on orignal code. See https://github.com/maxint-rd/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the version 3 GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TM1640_h
|
||||
#define TM1640_h
|
||||
|
||||
#include "TM16xx.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define TM1640_MAX_POS 16
|
||||
|
||||
class TM1640 : public TM16xx
|
||||
{
|
||||
public:
|
||||
// Instantiate a TM1640 module specifying data and clock pins, number of digits, the display state, the starting intensity (0-7).
|
||||
TM1640(gpio_num_t dataPin, gpio_num_t clockPin, uint8_t numDigits=16, bool activateDisplay = true, uint8_t intensity = 7);
|
||||
|
||||
protected:
|
||||
//virtual void bitDelay();
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
virtual void send(uint8_t data);
|
||||
};
|
||||
|
||||
#endif
|
||||
262
main/drivers/TM1640/TM16xx.cpp
Normal file
262
main/drivers/TM1640/TM16xx.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
TM16xx.h - Library for TM1637, TM1638 and similar chips.
|
||||
Modified by Maxint R&D. See https://github.com/maxint-rd/
|
||||
|
||||
Copyright (C) 2011 Ricardo Batista (rjbatista <at> gmail <dot> com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the version 3 GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TM16xx.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
TM16xx::TM16xx(gpio_num_t dataPin, gpio_num_t clockPin, gpio_num_t strobePin, uint8_t maxDisplays, uint8_t nDigitsUsed, bool activateDisplay, uint8_t intensity)
|
||||
{
|
||||
this->dataPin = dataPin;
|
||||
this->clockPin = clockPin;
|
||||
this->strobePin = strobePin;
|
||||
this->_maxDisplays = maxDisplays;
|
||||
this->digits = nDigitsUsed;
|
||||
|
||||
gpio_set_direction(dataPin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_direction(clockPin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_direction(strobePin, GPIO_MODE_OUTPUT);
|
||||
|
||||
gpio_set_level(strobePin, 1);
|
||||
gpio_set_level(clockPin, 1);
|
||||
|
||||
//sendCommand(TM16XX_CMD_DISPLAY | (activateDisplay ? 8 : 0) | min(7, intensity)); // display command: on or intensity
|
||||
|
||||
/*
|
||||
sendCommand(TM16XX_CMD_DATA_AUTO); // data command: set data mode to auto-increment write mode
|
||||
start();
|
||||
send(TM16XX_CMD_ADDRESS); // address command + address C0H
|
||||
for (int i = 0; i < 16; i++) { // TM1638 and TM1640 have 16 data addresses, TM1637 and TM1668 have less, but will wrap.
|
||||
send(0x00);
|
||||
}
|
||||
stop();
|
||||
*/
|
||||
// Note: calling these methods should be done in constructor of derived class in order to use properly initialized members!
|
||||
/*
|
||||
clearDisplay();
|
||||
setupDisplay(activateDisplay, intensity);
|
||||
*/
|
||||
}
|
||||
|
||||
void TM16xx::setupDisplay(bool active, uint8_t intensity)
|
||||
{
|
||||
sendCommand(TM16XX_CMD_DISPLAY | (active ? 8 : 0) | min(7, intensity));
|
||||
}
|
||||
|
||||
void TM16xx::clearDisplay()
|
||||
{ // Clear all data registers. The number of registers depends on the chip.
|
||||
// TM1638 (10x8): 10 segments per grid, stored in two bytes. The first byte contains the first 8 display segments, second byte has seg9+seg10 => 16 bytes
|
||||
// TM1640 (8x16): one byte per grid => 16 bytes
|
||||
// TM1637 (8x6): one byte per grid => 6 bytes
|
||||
// TM1668 (10x7 - 14x3): two bytes per grid => 14 bytes
|
||||
sendCommand(TM16XX_CMD_DATA_AUTO); // set auto increment addressing mode
|
||||
|
||||
// send the address followed by bulk-sending of the data to clear the display memory
|
||||
start();
|
||||
send(TM16XX_CMD_ADDRESS);
|
||||
for (int i = 0; i < _maxDisplays; i++) {
|
||||
send(0x00);
|
||||
if(_maxSegments>8)
|
||||
send(0x00); // send second byte (applicable to TM1638 and TM1668)
|
||||
}
|
||||
stop();
|
||||
|
||||
}
|
||||
|
||||
void TM16xx::setSegments(uint8_t segments, uint8_t position)
|
||||
{ // set 8 leds on common grd as specified
|
||||
// TODO: support 10-14 segments on chips like TM1638/TM1668
|
||||
if(position<_maxDisplays)
|
||||
sendData(position, segments);
|
||||
//sendData(TM16XX_CMD_ADDRESS | position, segments);
|
||||
}
|
||||
|
||||
void TM16xx::setSegments16(uint16_t segments, uint8_t position)
|
||||
{ // Some modules support more than 8 segments (e.g. 10 max for TM1638)
|
||||
// The position of the additional segments in the second data byte can be different per module,
|
||||
// For that reason this module has no implementation in the base class.
|
||||
// E.g. for TM1638/TM1668 segments 8-9 are in bits 0-1, for TM1630 segment 14 is in bit 5
|
||||
// This method assumes segments 0-7 to be in the lower byte and the extra segments in the upper byte
|
||||
// Depending on the module this method should shift the segments to the proper data position.
|
||||
}
|
||||
|
||||
|
||||
void TM16xx::sendChar(uint8_t pos, uint8_t data, bool dot)
|
||||
{
|
||||
/*
|
||||
if(pos<_maxDisplays)
|
||||
sendData(pos, data | (dot ? 0b10000000 : 0));
|
||||
*/
|
||||
setSegments(data | (dot ? 0b10000000 : 0), pos);
|
||||
}
|
||||
|
||||
void TM16xx::sendAsciiChar(uint8_t pos, char c, bool fDot)
|
||||
{ // Method to send an Ascii character to the display
|
||||
// This method is also called by TM16xxDisplay.print to display characters
|
||||
// The base class uses the default 7-segment font to find the LED pattern.
|
||||
// Derived classes for multi-segment displays or alternate layout displays can override this method
|
||||
sendChar(pos, TM16XX_FONT_DEFAULT[c-32], fDot);
|
||||
}
|
||||
|
||||
|
||||
void TM16xx::setDisplayDigit(uint8_t digit, uint8_t pos, bool dot, const uint8_t numberFont[])
|
||||
{
|
||||
sendChar(pos, numberFont[digit & 0xF], dot);
|
||||
}
|
||||
|
||||
void TM16xx::setDisplayToDecNumber(int nNumber, uint8_t bDots) // byte bDots=0
|
||||
{ // Function to display a decimal number on a n-digit clock display.
|
||||
// Kept simple to fit in ATtiny44A
|
||||
// For extended display features use the TM16xxDisplay class
|
||||
|
||||
// TODO: support large displays such as 8segx16 on TM1640
|
||||
for(uint8_t nPos=1; nPos<=digits; nPos++)
|
||||
{
|
||||
setDisplayDigit(nNumber % 10, digits - nPos, bDots&_BV(nPos));
|
||||
nNumber/=10;
|
||||
}
|
||||
}
|
||||
|
||||
void TM16xx::clearDisplayDigit(uint8_t pos, bool dot)
|
||||
{
|
||||
sendChar(pos, 0, dot);
|
||||
}
|
||||
|
||||
void TM16xx::setDisplay(const uint8_t values[], uint8_t size)
|
||||
{ // send an array of values to the display
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
sendChar(i, values[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TM16xx::setDisplayToString(const char* string, const uint32_t dots, const uint8_t pos, const uint8_t font[])
|
||||
{
|
||||
for (int i = 0; i < digits - pos; i++) {
|
||||
if (string[i] != '\0') {
|
||||
//sendChar(i + pos, pgm_read_byte_near(font+(string[i] - 32)), (dots & (1 << (digits - i - 1))) != 0);
|
||||
sendAsciiChar(i + pos, string[i], (dots & (1 << (digits - i - 1))) != 0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t TM16xx::getNumDigits()
|
||||
{ // get the number of digits used (needed by TM16xxDisplay to combine modules)
|
||||
return(digits);
|
||||
}
|
||||
|
||||
// key-scanning method, implemented in chip specific derived class
|
||||
uint32_t TM16xx::getButtons()
|
||||
{ // return state of up to 32 keys.
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Protected methods
|
||||
//
|
||||
|
||||
void TM16xx::bitDelay()
|
||||
{ // if needed derived classes can add a delay (eg. for TM1637)
|
||||
//delayMicroseconds(50);
|
||||
|
||||
// On fast MCUs like ESP32 a delay is required, especially when reading buttons
|
||||
// The TM1638 datasheet specifies a max clock speed of 1MHz.
|
||||
// Testing shows that without delay the CLK line exceeds 1.6 MHz on the ESP32. While displaying data still worked, reading buttons failed.
|
||||
// Adding a 1us delay gives clockpulses of about 1.75us-2.0us (~ 250kHz) on an ESP32 @240MHz and a similar delay on an ESP8266 @160Mhz.
|
||||
// An ESP32 running without delay at 240MHz gave a CLK of ~0.3us (~ 1.6MHz)
|
||||
// An ESP8266 running without delay at 160MHz gave a CLK of ~0.9us (~ 470kHz)
|
||||
// An ESP8266 running without delay at 80MHz gave a CLK of ~1.8us (~ 240kHz)
|
||||
#if F_CPU>100000000
|
||||
delayMicroseconds(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TM16xx::start()
|
||||
{ // if needed derived classes can use different patterns to start a command (eg. for TM1637)
|
||||
gpio_set_level(strobePin, 0);
|
||||
bitDelay();
|
||||
}
|
||||
|
||||
void TM16xx::stop()
|
||||
{ // if needed derived classes can use different patterns to stop a command (eg. for TM1637)
|
||||
gpio_set_level(strobePin, 1);
|
||||
bitDelay();
|
||||
}
|
||||
|
||||
void TM16xx::send(uint8_t data)
|
||||
{
|
||||
// MMOLE 180203: shiftout does something, but is not okay (tested on TM1668)
|
||||
//shiftOut(dataPin, clockPin, LSBFIRST, data);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
gpio_set_level(clockPin, 0);
|
||||
bitDelay();
|
||||
gpio_set_level(dataPin, data & 1);
|
||||
bitDelay();
|
||||
data >>= 1;
|
||||
gpio_set_level(clockPin, 1);
|
||||
bitDelay();
|
||||
}
|
||||
bitDelay(); // NOTE: TM1638 specifies a Twait between bytes of minimal 1us.
|
||||
}
|
||||
|
||||
void TM16xx::sendCommand(uint8_t cmd)
|
||||
{
|
||||
start();
|
||||
send(cmd);
|
||||
stop();
|
||||
}
|
||||
|
||||
void TM16xx::sendData(uint8_t address, uint8_t data)
|
||||
{
|
||||
sendCommand(TM16XX_CMD_DATA_FIXED); // use fixed addressing for data
|
||||
start();
|
||||
send(TM16XX_CMD_ADDRESS | address); // address command + address
|
||||
send(data);
|
||||
stop();
|
||||
}
|
||||
|
||||
uint8_t TM16xx::receive()
|
||||
{
|
||||
uint8_t temp = 0;
|
||||
|
||||
// Pull-up on
|
||||
gpio_set_direction(dataPin, GPIO_MODE_INPUT);
|
||||
gpio_set_level(dataPin, 1);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
temp >>= 1;
|
||||
|
||||
gpio_set_level(clockPin, 0);
|
||||
bitDelay(); // NOTE: on TM1637 reading keys should be slower than 250Khz (see datasheet p3)
|
||||
|
||||
if (gpio_get_level(dataPin)) {
|
||||
temp |= 0x80;
|
||||
}
|
||||
|
||||
gpio_set_level(clockPin, 1);
|
||||
bitDelay();
|
||||
}
|
||||
|
||||
// Pull-up off
|
||||
gpio_set_direction(dataPin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(dataPin, 0);
|
||||
|
||||
return temp;
|
||||
}
|
||||
138
main/drivers/TM1640/TM16xx.h
Normal file
138
main/drivers/TM1640/TM16xx.h
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
TM16xx.h - Library for TM1637, TM1638 and similar chips.
|
||||
|
||||
Copyright (C) 2011 Ricardo Batista <rjbatista at gmail dot com>
|
||||
Modified by Maxint R&D. See https://github.com/maxint-rd/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the version 3 GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TM16XX_h
|
||||
#define TM16XX_h
|
||||
|
||||
#if !defined(__max)
|
||||
// MMOLE 180325: min, max are no macro in ESP core 2.3.9 libraries, see https://github.com/esp8266/Arduino/issues/398
|
||||
// MMOLE 211229: Redefining min/max has issues in newer ESP cores with certain wifi libraries.
|
||||
// See definition as function template below.
|
||||
#define __min(a,b) ((a)<(b)?(a):(b))
|
||||
#define __max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#if !defined(_BV)
|
||||
// MMOLE 220909: _BV is not defined in Raspberry Pi Pico RP2040 core v2.4.0 or v2.5.2 by Earle F. Philhower
|
||||
// TM16xx uses it in some classes to determine button presses (TM1628/30/37/38/38Anode
|
||||
// Note that _BV works on int values (16-bit in Arduino). For that reason using bit() would be better.
|
||||
// This is how _BV is defined in the AVR libraries used within Arduino:
|
||||
#define _BV( x )( 1<<(x))
|
||||
#endif
|
||||
|
||||
#define TM16XX_CMD_DATA_AUTO 0x40
|
||||
#define TM16XX_CMD_DATA_READ 0x42 // command to read data used on two wire interfaces of TM1637
|
||||
#define TM16XX_CMD_DATA_FIXED 0x44
|
||||
#define TM16XX_CMD_DISPLAY 0x80
|
||||
#define TM16XX_CMD_ADDRESS 0xC0
|
||||
|
||||
// Common display modes for selecting different GRID x SEGMENT configuration
|
||||
// Used by e.g. TM1618, TM1620, TM1628, TM1630, TM1668
|
||||
// (note: TM1652 combined display control and uses a single bit to designate 5x8 or 6x8)
|
||||
#define TM16XX_CMD_MODE_4GRID 0x00
|
||||
#define TM16XX_CMD_MODE_5GRID 0x01 // TM1630: 4x8 - 5x7
|
||||
#define TM16XX_CMD_MODE_6GRID 0x02 // TM1620: 4x10 - 6x8
|
||||
#define TM16XX_CMD_MODE_7GRID 0x03 // TM1628, TM1668: 4x13 - 7x10, TM1624: 4x14 - 7x11, TM1618: 4x8 - 7x5
|
||||
|
||||
#include "TM16xxFonts.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
class TM16xx
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Instantiate a TM16xx module specifying data, clock and strobe pins (no strobe on some modules),
|
||||
* the maximum number of displays supported by the chip (as provided by derived chip specific class),
|
||||
* the number of digits used to display numbers or text,
|
||||
* display state and the starting intensity (0-7).
|
||||
*/
|
||||
TM16xx(gpio_num_t dataPin, gpio_num_t clockPin, gpio_num_t strobePin, uint8_t maxDisplays, uint8_t nDigitsUsed, bool activateDisplay=true, uint8_t intensity=7);
|
||||
|
||||
/** Set the display (segments and LEDs) active or off and intensity (range from 0-7). */
|
||||
virtual void setupDisplay(bool active, uint8_t intensity);
|
||||
|
||||
/** Clear the display */
|
||||
virtual void clearDisplay();
|
||||
|
||||
/** Set segments of the display */
|
||||
virtual void setSegments(uint8_t segments, uint8_t position);
|
||||
virtual void setSegments16(uint16_t segments, uint8_t position); // some modules support more than 8 segments
|
||||
|
||||
// Basic display functions. For additional display features use the TM16xxDisplay class
|
||||
/** Set a single display at pos (starting at 0) to a digit (left to right) */
|
||||
virtual void setDisplayDigit(uint8_t digit, uint8_t pos=0, bool dot=false, const uint8_t numberFont[] = TM16XX_NUMBER_FONT);
|
||||
/** Set the display to a decimal number */
|
||||
virtual void setDisplayToDecNumber(int nNumber, uint8_t bDots=0);
|
||||
/** Clear a single display at pos (starting at 0, left to right) */
|
||||
virtual void clearDisplayDigit(uint8_t pos, bool dot=false);
|
||||
/** Set the display to the values (left to right) */
|
||||
virtual void setDisplay(const uint8_t values[], uint8_t size=8);
|
||||
|
||||
/** Set the display to the string (defaults to built in font) */
|
||||
virtual void setDisplayToString(const char* string, const uint32_t dots=0, const uint8_t pos=0, const uint8_t font[] = TM16XX_FONT_DEFAULT);
|
||||
virtual void sendChar(uint8_t pos, uint8_t data, bool dot); // made public to allow calling from TM16xxDisplay
|
||||
virtual uint8_t getNumDigits(); // added as public menthod to allow calling from TM16xxDisplay
|
||||
virtual void sendAsciiChar(uint8_t pos, char c, bool dot); // made public to allow calling from TM16xxDisplay
|
||||
|
||||
// Key-scanning functions
|
||||
// Note: not all TM16xx chips support key-scanning and sizes are different per chip
|
||||
// Up to 32 key states are supported, but specific chips may support less keys or less combinations
|
||||
// The chip specific derived class method will return a 32-bit value representing the state of each key, containing 0 if no key is pressed
|
||||
virtual uint32_t getButtons(); // return state of up to 32 keys.
|
||||
|
||||
protected:
|
||||
// virtual void sendChar(byte pos, byte data, bool dot);
|
||||
virtual void bitDelay();
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
virtual void send(uint8_t data);
|
||||
virtual void sendCommand(uint8_t led);
|
||||
virtual void sendData(uint8_t add, uint8_t data);
|
||||
virtual uint8_t receive();
|
||||
|
||||
#if !defined(max)
|
||||
// MMOLE 211229: use c++ function templates to implement our own min/max, as redefining them wont work in newer ESP cores when using certain wifi libraries
|
||||
// NOTE: min, max are no macro in ESP core 2.3.9 libraries, see https://github.com/esp8266/Arduino/issues/398
|
||||
// See also
|
||||
// https://www.cplusplus.com/doc/oldtutorial/templates/
|
||||
// https://www.alltestanswers.com/c-templates-for-the-two-functions-minimum-and-maximum/
|
||||
// https://www.learncpp.com/cpp-tutorial/function-templates-with-multiple-template-types/
|
||||
// MMOLE 220814: Arduino IDE 1.8.12 for LGT328P required "#if !defined(max)" to prevent compilation errors
|
||||
template <typename T, typename U>
|
||||
auto max(T x, U y) -> decltype(x>y ? x : y)
|
||||
{
|
||||
return x>y ? x : y;
|
||||
}
|
||||
template <typename T, typename U>
|
||||
auto min(T x, U y) -> decltype(x>y ? x : y)
|
||||
{
|
||||
return x<y ? x : y;
|
||||
}
|
||||
#endif // !defined(max)
|
||||
|
||||
uint8_t _maxDisplays=2; // maximum number of digits (grids), chip-dependent
|
||||
uint8_t _maxSegments=8; // maximum number of segments per display, chip-dependent
|
||||
|
||||
uint8_t digits; // number of digits in the display, module dependent
|
||||
gpio_num_t dataPin;
|
||||
gpio_num_t clockPin;
|
||||
gpio_num_t strobePin;
|
||||
};
|
||||
|
||||
#endif
|
||||
313
main/drivers/TM1640/TM16xxFonts.h
Normal file
313
main/drivers/TM1640/TM16xxFonts.h
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
TM16xxFonts.h - Font definition for TM16xx.
|
||||
|
||||
Copyright (C) 2011 Ricardo Batista (rjbatista <at> gmail <dot> com)
|
||||
Modified by Maxint R&D. See https://github.com/maxint-rd/
|
||||
- Improvements for 7-segment alpha-numeric font
|
||||
- Additional 15-segment font
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the version 3 GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Segment labels
|
||||
-- A --
|
||||
| |
|
||||
F B
|
||||
-- G --
|
||||
E C
|
||||
| |
|
||||
-- D -- .DP
|
||||
|
||||
The bits are displayed by the mapping below:
|
||||
-- 0 --
|
||||
| |
|
||||
5 1
|
||||
-- 6 --
|
||||
4 2
|
||||
| |
|
||||
-- 3 -- .7
|
||||
|
||||
|
||||
Common pinout 7-segments x 4 digits:
|
||||
12 11 10 9 8 7
|
||||
| | | | | |
|
||||
+------------------------+ 12, 9, 8, 6: GRD1-GRD4
|
||||
| -- -- -- -- |
|
||||
| | | | | | | | | | 1: SEG_E 5: SEG_G
|
||||
| -- -- -- -- | 2: SEG_D 7: SEG_B
|
||||
| | | | | | | | | | 3: SEG_DP 10: SEG_F
|
||||
| -- . -- . -- . -- . | 4: SEG_C 11: SEG_A
|
||||
+------------------------+
|
||||
| | | | | |
|
||||
1 2 3 4 5 6
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TM16XXFonts_h
|
||||
#define TM16XXFonts_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// definition for standard hexadecimal numbers
|
||||
const uint8_t TM16XX_NUMBER_FONT[] = {
|
||||
0b00111111, // 0
|
||||
0b00000110, // 1
|
||||
0b01011011, // 2
|
||||
0b01001111, // 3
|
||||
0b01100110, // 4
|
||||
0b01101101, // 5
|
||||
0b01111101, // 6
|
||||
0b00000111, // 7
|
||||
0b01111111, // 8
|
||||
0b01101111, // 9
|
||||
0b01110111, // A
|
||||
0b01111100, // B
|
||||
0b00111001, // C
|
||||
0b01011110, // D
|
||||
0b01111001, // E
|
||||
0b01110001 // F
|
||||
};
|
||||
|
||||
const uint8_t MINUS = 0b01000000;
|
||||
|
||||
// definition for error text
|
||||
const uint8_t TM16XX_ERROR_DATA[] = {
|
||||
0b01111001, // E
|
||||
0b01010000, // r
|
||||
0b01010000, // r
|
||||
0b01011100, // o
|
||||
0b01010000, // r
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
// Definition for the displayable ASCII chars
|
||||
// (note that non-alpha-numeric characters may display as a space or a simple dash)
|
||||
const uint8_t TM16XX_FONT_DEFAULT[] = {
|
||||
0b00000000, // (32) <space>
|
||||
0b10000110, // (33) !
|
||||
0b00100010, // (34) "
|
||||
0b01111110, // (35) #
|
||||
0b01101101, // (36) $
|
||||
0b00000000, // (37) %
|
||||
0b00000000, // (38) &
|
||||
0b00000010, // (39) '
|
||||
0b00110000, // (40) (
|
||||
0b00000110, // (41) )
|
||||
0b01100011, // (42) *
|
||||
0b00000000, // (43) +
|
||||
0b00000100, // (44) ,
|
||||
0b01000000, // (45) -
|
||||
0b10000000, // (46) .
|
||||
0b01010010, // (47) /
|
||||
0b00111111, // (48) 0
|
||||
0b00000110, // (49) 1
|
||||
0b01011011, // (50) 2
|
||||
0b01001111, // (51) 3
|
||||
0b01100110, // (52) 4
|
||||
0b01101101, // (53) 5
|
||||
0b01111101, // (54) 6
|
||||
0b00100111, // (55) 7
|
||||
0b01111111, // (56) 8
|
||||
0b01101111, // (57) 9
|
||||
0b00000000, // (58) :
|
||||
0b00000000, // (59) ;
|
||||
0b00000000, // (60) <
|
||||
0b01001000, // (61) =
|
||||
0b00000000, // (62) >
|
||||
0b01010011, // (63) ?
|
||||
0b01011111, // (64) @
|
||||
0b01110111, // (65) A
|
||||
0b01111111, // (66) B
|
||||
0b00111001, // (67) C
|
||||
0b00111111, // (68) D
|
||||
0b01111001, // (69) E
|
||||
0b01110001, // (70) F
|
||||
0b00111101, // (71) G
|
||||
0b01110110, // (72) H
|
||||
0b00000110, // (73) I
|
||||
0b00011110, // (74) J
|
||||
0b01101001, // (75) K
|
||||
0b00111000, // (76) L
|
||||
0b00010101, // (77) M
|
||||
0b00110111, // (78) N
|
||||
0b00111111, // (79) O
|
||||
0b01110011, // (80) P
|
||||
0b01100111, // (81) Q
|
||||
0b00110001, // (82) R
|
||||
0b01101101, // (83) S
|
||||
0b01111000, // (84) T
|
||||
0b00111110, // (85) U
|
||||
0b00101010, // (86) V
|
||||
0b00011101, // (87) W
|
||||
0b01110110, // (88) X
|
||||
0b01101110, // (89) Y
|
||||
0b01011011, // (90) Z
|
||||
0b00111001, // (91) [
|
||||
0b01100100, // (92) \ (this can't be the last char on a line, even in comment or it'll concat)
|
||||
0b00001111, // (93) ]
|
||||
0b00000000, // (94) ^
|
||||
0b00001000, // (95) _
|
||||
0b00100000, // (96) `
|
||||
0b01011111, // (97) a
|
||||
0b01111100, // (98) b
|
||||
0b01011000, // (99) c
|
||||
0b01011110, // (100) d
|
||||
0b01111011, // (101) e
|
||||
0b00110001, // (102) f
|
||||
0b01101111, // (103) g
|
||||
0b01110100, // (104) h
|
||||
0b00000100, // (105) i
|
||||
0b00001110, // (106) j
|
||||
0b01110101, // (107) k
|
||||
0b00110000, // (108) l
|
||||
0b01010101, // (109) m
|
||||
0b01010100, // (110) n
|
||||
0b01011100, // (111) o
|
||||
0b01110011, // (112) p
|
||||
0b01100111, // (113) q
|
||||
0b01010000, // (114) r
|
||||
0b01101101, // (115) s
|
||||
0b01111000, // (116) t
|
||||
0b00011100, // (117) u
|
||||
0b00101010, // (118) v
|
||||
0b00011101, // (119) w
|
||||
0b01110110, // (120) x
|
||||
0b01101110, // (121) y
|
||||
0b01000111, // (122) z
|
||||
0b01000110, // (123) {
|
||||
0b00000110, // (124) |
|
||||
0b01110000, // (125) }
|
||||
0b00000001, // (126) ~
|
||||
};
|
||||
|
||||
/*
|
||||
Definition for the displayable ASCII chars on a 15-segment display.
|
||||
|
||||
Segment labels and bit-assignment:
|
||||
|---A---| Bit Segment Bit Segment
|
||||
|\ | /| [0] A [ 8] G2
|
||||
F H J K B [1] B [ 9] H
|
||||
| \|/ | [2] C [10] J
|
||||
|-G1-G2-| [3] D [11] K
|
||||
| /|\ | [4] E [12] L
|
||||
E N M L C [5] F [13] M
|
||||
|/ | \| [6] G1 [14] N
|
||||
|---D---| DP [7] DP
|
||||
|
||||
14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
N M L K J H G2 DP G1 F E D C B A
|
||||
|
||||
Note: For compatibility bits 0-7 are in the same order as A-G on a 7-segment display.
|
||||
Most characters in the font below are based on their 7-seg counterparts to keep styling alike.
|
||||
*/
|
||||
const uint16_t TM16XX_FONT_15SEG[] = {
|
||||
0b00000000, // (32) <space>
|
||||
0b10010010000000, // (33) !
|
||||
0b00100010, // (34) "
|
||||
0b10010101110001, // (35) #
|
||||
0b10010101101101, // (36) $
|
||||
0b100100000100100, // (37) %
|
||||
0b1101001011001, // (38) &
|
||||
0b00000010, // (39) '
|
||||
0b00110000, // (40) (
|
||||
0b00000110, // (41) )
|
||||
0b111111101000000, // (42) *
|
||||
0b010010101000000, // (43) +
|
||||
0b100000000000000, // (44) ,
|
||||
0b01000000, // (45) -
|
||||
0b10000000, // (46) .
|
||||
0b0100100000000000, // (47) /
|
||||
0b00111111, // (48) 0
|
||||
0b00000110, // (49) 1
|
||||
0b101011011, // (50) 2
|
||||
0b101001111, // (51) 3
|
||||
0b101100110, // (52) 4
|
||||
0b101101101, // (53) 5
|
||||
0b101111101, // (54) 6
|
||||
0b00100111, // (55) 7
|
||||
0b101111111, // (56) 8
|
||||
0b101101111, // (57) 9
|
||||
0b10000000, // (58) :
|
||||
0b100000000000010, // (59) ;
|
||||
0b1100000000000, // (60) <
|
||||
0b101001000, // (61) =
|
||||
0b100001000000000, // (62) >
|
||||
0b10000110000011, // (63) ?
|
||||
0b100111011, // (64) @
|
||||
0b101110111, // (65) A
|
||||
0b10010100001111, // (66) B
|
||||
0b00111001, // (67) C
|
||||
0b10010000001111, // (68) D
|
||||
0b101111001, // (69) E
|
||||
0b101110001, // (70) F
|
||||
0b100111101, // (71) G
|
||||
0b101110110, // (72) H
|
||||
0b10010000001001, // (73) I
|
||||
0b00011110, // (74) J
|
||||
0b01100001110000, // (75) K
|
||||
0b00111000, // (76) L
|
||||
0b101000110110, // (77) M
|
||||
0b1001000110110, // (78) N
|
||||
0b00111111, // (79) O
|
||||
0b101110011, // (80) P
|
||||
0b1000000111111, // (81) Q
|
||||
0b1000101110011, // (82) R
|
||||
0b101101101, // (83) S
|
||||
0b010010000000001, // (84) T
|
||||
0b00111110, // (85) U
|
||||
0b1001000000110, // (86) V
|
||||
0b101000000110110, // (87) W
|
||||
0b101101000000000, // (88) X
|
||||
0b00010101000000000, // (89) Y
|
||||
0b100100000001001, // (90) Z
|
||||
0b00111001, // (91) [
|
||||
0b1001000000000, // (92) \ (this can't be the last char on a line, even in comment or it'll concat)
|
||||
0b00001111, // (93) ]
|
||||
0b101000000000000, // (94) ^
|
||||
0b00001000, // (95) _
|
||||
0b1000000000, // (96) `
|
||||
0b101011111, // (97) a
|
||||
0b101111100, // (98) b
|
||||
0b101011000, // (99) c
|
||||
0b101011110, // (100) d
|
||||
0b101111011, // (101) e
|
||||
0b01110001, // (102) f
|
||||
0b101101111, // (103) g
|
||||
0b101110100, // (104) h
|
||||
0b00000100, // (105) i
|
||||
0b00001110, // (106) j
|
||||
0b100001110100, // (107) k
|
||||
0b00110000, // (108) l
|
||||
0b101000110110, // (109) m
|
||||
0b10000100000100, // (110) n
|
||||
0b101011100, // (111) o
|
||||
0b101110011, // (112) p
|
||||
0b101100111, // (113) q
|
||||
0b101010000, // (114) r
|
||||
0b101101101, // (115) s
|
||||
0b01111000, // (116) t
|
||||
0b00011100, // (117) u
|
||||
0b100000000010000, // (118) v
|
||||
0b101000000010100, // (119) w
|
||||
0b101101000000000, // (120) x
|
||||
0b100101000000000, // (121) y
|
||||
0b100100000001001, // (122) z
|
||||
0b100001001001001, // (123) {
|
||||
0b010010000000000, // (124) |
|
||||
0b1100100001001, // (125) }
|
||||
0b00000001, // (126) ~
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
20
main/drivers/all.cpp
Normal file
20
main/drivers/all.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "all.h"
|
||||
|
||||
void init_drivers() {
|
||||
init_i2c();
|
||||
// init char_lcd so we can use it to report other initialization errors.
|
||||
init_lcd();
|
||||
init_nvs();
|
||||
init_star_code_system();
|
||||
// init the bottom half so that we can get user input.
|
||||
init_bottom_half();
|
||||
init_sd();
|
||||
init_speaker();
|
||||
init_sseg();
|
||||
init_game_timers();
|
||||
init_tft();
|
||||
init_leds();
|
||||
init_power_board();
|
||||
|
||||
set_lcd_header_enabled(true);
|
||||
}
|
||||
21
main/drivers/all.h
Normal file
21
main/drivers/all.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef ALL_H
|
||||
#define ALL_H
|
||||
|
||||
#include "bottom_half.h"
|
||||
#include "char_lcd.h"
|
||||
#include "game_timer.h"
|
||||
#include "i2c.h"
|
||||
#include "leds.h"
|
||||
#include "nvs.h"
|
||||
#include "power.h"
|
||||
#include "sd.h"
|
||||
#include "speaker.h"
|
||||
#include "sseg.h"
|
||||
#include "starcode.h"
|
||||
#include "state_tracking.h"
|
||||
#include "tft.h"
|
||||
#include "wires.h"
|
||||
|
||||
void init_drivers();
|
||||
|
||||
#endif /* ALL_H */
|
||||
403
main/drivers/bottom_half.cpp
Normal file
403
main/drivers/bottom_half.cpp
Normal file
@ -0,0 +1,403 @@
|
||||
#include "bottom_half.h"
|
||||
#include <esp_log.h>
|
||||
#include "state_tracking.h"
|
||||
#include "starcode.h"
|
||||
|
||||
static const char *TAG = "bottom_half";
|
||||
|
||||
static uint16_t keypad_state;
|
||||
static uint8_t button_state;
|
||||
static uint8_t switch_state;
|
||||
static uint8_t switch_touch_state;
|
||||
static uint8_t touch_state;
|
||||
|
||||
static uint16_t keypad_pressed;
|
||||
static uint8_t button_pressed;
|
||||
static uint8_t switch_flipped_up;
|
||||
static uint8_t switch_touch_pressed;
|
||||
static uint8_t touch_pressed;
|
||||
|
||||
static uint16_t keypad_released;
|
||||
static uint8_t button_released;
|
||||
static uint8_t switch_flipped_down;
|
||||
static uint8_t switch_touch_released;
|
||||
static uint8_t touch_released;
|
||||
|
||||
/// read buffer
|
||||
static uint8_t buf[8];
|
||||
|
||||
static void poll_bottom_task(void *arg);
|
||||
static void receive_keypad();
|
||||
static void receive_button_switch();
|
||||
static void receive_touch();
|
||||
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
// no reply neccesary
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: add intrupt on bottom half delta pin
|
||||
// static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||
// {
|
||||
// // TODO: do somthing
|
||||
// ESP_LOGI("BOTTOM_HALF", "Hit");
|
||||
// }
|
||||
|
||||
void init_bottom_half() {
|
||||
ESP_LOGI(TAG, "Initializing bottom half...");
|
||||
|
||||
gpio_config_t int_conf = {
|
||||
.pin_bit_mask = 1ULL << BOTTOM_PIN_INTERUPT,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&int_conf));
|
||||
|
||||
// TODO: do interupt stuff.
|
||||
// ESP_ERROR_CHECK(gpio_intr_enable(BOTTOM_PIN_INTERUPT));
|
||||
// ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG))
|
||||
//install gpio isr service
|
||||
// gpio_install_isr_service(0);
|
||||
//hook isr handler for specific gpio pin
|
||||
// gpio_isr_handler_add(BOTTOM_INTERUPT_PIN, gpio_isr_handler, NULL);
|
||||
|
||||
receive_keypad();
|
||||
receive_button_switch();
|
||||
receive_touch();
|
||||
|
||||
xTaskCreate(poll_bottom_task, "poll_bottom", 4096, NULL, 10, NULL);
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
ESP_LOGI(TAG, "Bottom half initialized!");
|
||||
}
|
||||
|
||||
static uint8_t receive_delta(void) {
|
||||
uint8_t reg = 1;
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
if (result != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void receive_keypad(void) {
|
||||
// TODO: change the bottom half polling scheme from a state-based protocol to an event based protocol
|
||||
uint8_t reg = 2;
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS));
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
if (result != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
uint16_t new_keypad_state = buf[0] | (buf[1] << 8);
|
||||
uint16_t just_pressed = new_keypad_state & ~keypad_state;
|
||||
if (is_state_tracking() && just_pressed) {
|
||||
char buf[6];
|
||||
sprintf(buf, "%d", just_pressed);
|
||||
event_occured("KP_PRESS", buf);
|
||||
}
|
||||
|
||||
uint16_t just_released = ~new_keypad_state & keypad_state;
|
||||
if (is_state_tracking() && just_released) {
|
||||
char buf[6];
|
||||
sprintf(buf, "%d", just_released);
|
||||
event_occured("KP_RELEASE", buf);
|
||||
}
|
||||
|
||||
star_code_handle_keypad(&just_pressed, &just_released);
|
||||
|
||||
keypad_pressed |= just_pressed;
|
||||
keypad_released |= just_released;
|
||||
keypad_state = new_keypad_state;
|
||||
}
|
||||
|
||||
static void receive_button_switch(void) {
|
||||
uint8_t reg = 3;
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS));
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
if (result != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t new_button_state = buf[1] & 0xF;
|
||||
uint8_t new_switch_state = (~buf[0]) & 0xF;
|
||||
uint8_t new_switch_touch_state = (buf[1] >> 4) & 0xF;
|
||||
|
||||
// button
|
||||
uint8_t just_pressed = new_button_state & ~button_state;
|
||||
button_pressed |= just_pressed;
|
||||
if (is_state_tracking() && just_pressed) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", just_pressed);
|
||||
event_occured("BTN_PRESS", buf);
|
||||
}
|
||||
|
||||
uint8_t just_released = ~new_button_state & button_state;
|
||||
button_released |= just_released;
|
||||
if (is_state_tracking() && just_released) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", just_released);
|
||||
event_occured("BTN_RELEASE", buf);
|
||||
}
|
||||
|
||||
button_state = new_button_state;
|
||||
|
||||
// switch
|
||||
uint8_t just_flipped_up = new_switch_state & ~switch_state;
|
||||
switch_flipped_up |= just_flipped_up;
|
||||
if (is_state_tracking() && just_flipped_up) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", just_flipped_up);
|
||||
event_occured("SW_UP", buf);
|
||||
}
|
||||
|
||||
uint8_t just_flipped_down = ~new_switch_state & switch_state;
|
||||
switch_flipped_down |= just_flipped_down;
|
||||
if (is_state_tracking() && just_flipped_down) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", just_flipped_down);
|
||||
event_occured("SW_DOWN", buf);
|
||||
}
|
||||
|
||||
switch_state = new_switch_state;
|
||||
|
||||
// switch touch
|
||||
uint8_t touch_just_pressed = new_switch_touch_state & ~switch_touch_state;
|
||||
switch_touch_pressed |= touch_just_pressed;
|
||||
if (is_state_tracking() && touch_just_pressed) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", touch_just_pressed);
|
||||
event_occured("SW_TOUCH", buf);
|
||||
}
|
||||
|
||||
uint8_t touch_just_released = ~new_switch_touch_state & switch_touch_state;
|
||||
switch_touch_released |= touch_just_released;
|
||||
if (is_state_tracking() && touch_just_released) {
|
||||
char buf[4];
|
||||
sprintf(buf, "%d", touch_just_released);
|
||||
event_occured("SW_UNTOUCH", buf);
|
||||
}
|
||||
|
||||
switch_touch_state = new_switch_touch_state;
|
||||
}
|
||||
|
||||
static void receive_touch(void) {
|
||||
uint8_t reg = 4;
|
||||
buf[0] = 0;
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
bool new_touch_state = buf[0] != 0;
|
||||
|
||||
bool just_pressed = new_touch_state & !touch_state;
|
||||
touch_pressed |= just_pressed;
|
||||
if (is_state_tracking() && just_pressed) {
|
||||
event_occured("FINGER_TOUCHED", NULL);
|
||||
}
|
||||
|
||||
bool just_released = (!new_touch_state) & touch_state;
|
||||
touch_released |= just_released;
|
||||
if (is_state_tracking() && just_released) {
|
||||
event_occured("FINGER_UNTOUCHED", NULL);
|
||||
}
|
||||
|
||||
touch_state = new_touch_state;
|
||||
}
|
||||
|
||||
static void poll_bottom_task(void *arg) {
|
||||
// TODO: if using an interupt, switch this to use a queue
|
||||
while (1) {
|
||||
bool new_data = gpio_get_level(BOTTOM_PIN_INTERUPT) == 0;
|
||||
if (new_data) {
|
||||
uint8_t delta = receive_delta();
|
||||
// ESP_LOGI(_TAG, "delta: %d", delta);
|
||||
if (delta == 0) ESP_LOGW(TAG, "delta pin was low, but delta register returned 0");
|
||||
|
||||
if (delta & (1 << DELTA_BIT_KP)) receive_keypad();
|
||||
if (delta & (1 << DELTA_BIT_BUTTON_SWITCH)) receive_button_switch();
|
||||
if (delta & (1 << DELTA_BIT_TOUCH)) receive_touch();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void clear_all_pressed_released(void) {
|
||||
keypad_pressed = 0;
|
||||
button_pressed = 0;
|
||||
switch_flipped_up = 0;
|
||||
touch_pressed = 0;
|
||||
|
||||
keypad_released = 0;
|
||||
button_released = 0;
|
||||
switch_flipped_down = 0;
|
||||
touch_released = 0;
|
||||
}
|
||||
|
||||
// TODO: this is public, but it won't need to be after the event-based protocol refactor
|
||||
bool take_key(KeypadKey* kp, uint16_t* keypad_bitfield) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int bit_selector = (1 << i);
|
||||
if ((*keypad_bitfield) & bit_selector) {
|
||||
if (kp != nullptr) {
|
||||
*kp = (KeypadKey) i;
|
||||
}
|
||||
// clear bit
|
||||
*keypad_bitfield &= ~bit_selector;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_keypad_pressed(KeypadKey* kp) {
|
||||
return take_key(kp, &keypad_pressed);
|
||||
}
|
||||
bool get_keypad_released(KeypadKey* kp) {
|
||||
return take_key(kp, &keypad_released);
|
||||
}
|
||||
|
||||
char char_of_keypad_key(KeypadKey kp) {
|
||||
switch (kp) {
|
||||
case KeypadKey::k1:
|
||||
return '1';
|
||||
case KeypadKey::k2:
|
||||
return '2';
|
||||
case KeypadKey::k3:
|
||||
return '3';
|
||||
case KeypadKey::k4:
|
||||
return '4';
|
||||
case KeypadKey::k5:
|
||||
return '5';
|
||||
case KeypadKey::k6:
|
||||
return '6';
|
||||
case KeypadKey::k7:
|
||||
return '7';
|
||||
case KeypadKey::k8:
|
||||
return '8';
|
||||
case KeypadKey::k9:
|
||||
return '9';
|
||||
case KeypadKey::k0:
|
||||
return '0';
|
||||
case KeypadKey::ka:
|
||||
return 'A';
|
||||
case KeypadKey::kb:
|
||||
return 'B';
|
||||
case KeypadKey::kc:
|
||||
return 'C';
|
||||
case KeypadKey::kd:
|
||||
return 'D';
|
||||
case KeypadKey::star:
|
||||
return '*';
|
||||
case KeypadKey::pound:
|
||||
return '#';
|
||||
default:
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static bool _take_button(ButtonKey* button, uint8_t* button_bitfield) {
|
||||
if (((*button_bitfield) & 0xF) == 0) return false;
|
||||
|
||||
// scan the 4 button bits for one that is set
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int bit_selector = (1 << i);
|
||||
if ((*button_bitfield) & bit_selector) {
|
||||
if (button != nullptr) {
|
||||
*button = (ButtonKey) i;
|
||||
}
|
||||
// clear bit
|
||||
*button_bitfield &= ~bit_selector;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool _take_switch(SwitchKey* switch_, uint8_t* switch_bitfield) {
|
||||
if (((*switch_bitfield) & 0xF) == 0) return false;
|
||||
|
||||
// scan the 4 switch bits for one that is set
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int bit_selector = (1 << i);
|
||||
if ((*switch_bitfield) & bit_selector) {
|
||||
if (switch_ != nullptr) {
|
||||
*switch_ = (SwitchKey) i;
|
||||
}
|
||||
// clear bit
|
||||
*switch_bitfield &= ~bit_selector;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_button_pressed(ButtonKey* button) {
|
||||
return _take_button(button, &button_pressed);
|
||||
}
|
||||
bool get_button_released(ButtonKey* button) {
|
||||
return _take_button(button, &button_released);
|
||||
}
|
||||
uint8_t get_button_state() {
|
||||
return button_state;
|
||||
}
|
||||
|
||||
bool get_switch_flipped_up(SwitchKey* switch_) {
|
||||
return _take_switch(switch_, &switch_flipped_up);
|
||||
}
|
||||
bool get_switch_flipped_down(SwitchKey* switch_) {
|
||||
return _take_switch(switch_, &switch_flipped_down);
|
||||
}
|
||||
bool get_switch_flipped(SwitchKey* switch_) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int bit_selector = (1 << i);
|
||||
if (
|
||||
((switch_flipped_up & bit_selector) != 0) ||
|
||||
((switch_flipped_down & bit_selector) != 0)
|
||||
) {
|
||||
if (switch_ != nullptr) {
|
||||
*switch_ = (SwitchKey) i;
|
||||
}
|
||||
// clear bit
|
||||
switch_flipped_up &= ~bit_selector;
|
||||
switch_flipped_down &= ~bit_selector;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
uint8_t get_switch_state() {
|
||||
return switch_state;
|
||||
}
|
||||
|
||||
bool get_switch_touch_pressed(SwitchKey* switch_) {
|
||||
return _take_switch(switch_, &switch_touch_pressed);
|
||||
}
|
||||
bool get_switch_touch_released(SwitchKey* switch_) {
|
||||
return _take_switch(switch_, &switch_touch_released);
|
||||
}
|
||||
uint8_t get_switch_touch_state(){
|
||||
return switch_touch_state;
|
||||
}
|
||||
|
||||
bool get_touch_state(void) {
|
||||
return touch_state;
|
||||
}
|
||||
bool get_touch_pressed(void) {
|
||||
bool return_ = touch_pressed;
|
||||
touch_pressed = false;
|
||||
return return_;
|
||||
}
|
||||
bool get_touch_released(void) {
|
||||
bool return_ = touch_released;
|
||||
touch_released = false;
|
||||
return return_;
|
||||
}
|
||||
139
main/drivers/bottom_half.h
Normal file
139
main/drivers/bottom_half.h
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef BOTTOM_HALF_HPP
|
||||
#define BOTTOM_HALF_HPP
|
||||
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#define BOTTOM_I2C_NUM I2C_NUM_0
|
||||
#define BOTTOM_I2C_ADDR 126
|
||||
#define BOTTOM_PIN_INTERUPT GPIO_NUM_13
|
||||
|
||||
#define DELTA_BIT_KP 0
|
||||
#define DELTA_BIT_BUTTON_SWITCH 1
|
||||
#define DELTA_BIT_TOUCH 2
|
||||
|
||||
/// @brief An enum for the possible keypad buttons.
|
||||
typedef enum {
|
||||
kd = 0,
|
||||
pound = 1,
|
||||
k0 = 2,
|
||||
star = 3,
|
||||
kc = 4,
|
||||
k9 = 5,
|
||||
k8 = 6,
|
||||
k7 = 7,
|
||||
kb = 8,
|
||||
k6 = 9,
|
||||
k5 = 10,
|
||||
k4 = 11,
|
||||
ka = 12,
|
||||
k3 = 13,
|
||||
k2 = 14,
|
||||
k1 = 15,
|
||||
} KeypadKey;
|
||||
|
||||
/// @brief An enum for the possible buttons.
|
||||
typedef enum {
|
||||
b1 = 0,
|
||||
b2 = 1,
|
||||
b3 = 2,
|
||||
b4 = 3,
|
||||
button_green = 0,
|
||||
button_red = 1,
|
||||
button_yellow = 2,
|
||||
button_blue = 3,
|
||||
} ButtonKey;
|
||||
|
||||
/// @brief An enum for the possible switches.
|
||||
typedef enum {
|
||||
s1 = 0,
|
||||
s2 = 1,
|
||||
s3 = 2,
|
||||
s4 = 3,
|
||||
} SwitchKey;
|
||||
|
||||
/// @brief Initializes communication with the bottom half.
|
||||
void init_bottom_half();
|
||||
|
||||
/// Clears all pending pressed/released switches/buttons/keys/touch sensors.
|
||||
void clear_all_pressed_released();
|
||||
|
||||
/// @brief Gets the key that was just pressed (if any)
|
||||
/// @param kp an OUT variable for the key that was pressed (if any)
|
||||
/// @return true if there was a key that was just pressed
|
||||
bool get_keypad_pressed(KeypadKey* kp);
|
||||
/// @brief Gets the key that was just released (if any)
|
||||
/// @param kp an OUT variable for the key that was released (if any)
|
||||
/// @return true if there was a key that was just released
|
||||
bool get_keypad_released(KeypadKey* kp);
|
||||
/// @brief Converts a `KeypadKey` to a char
|
||||
/// @param kp The value to convert
|
||||
/// @return The char representing the key
|
||||
char char_of_keypad_key(KeypadKey kp);
|
||||
// TODO: add a get_keypad state?
|
||||
|
||||
|
||||
/// @brief Gets the button that was just pressed (if any)
|
||||
/// @param button an OUT variable for the button that was pressed (if any)
|
||||
/// @return true if there was a button that was just pressed
|
||||
bool get_button_pressed(ButtonKey* button);
|
||||
/// @brief Gets the button that was just released (if any)
|
||||
/// @param button an OUT variable for the button that was released (if any)
|
||||
/// @return true if there was a button that was just released
|
||||
bool get_button_released(ButtonKey* button);
|
||||
/// @brief Gets the raw state of the buttons b1-b4 as bitflags.
|
||||
/// B1 is MSB and B4 is LSB.
|
||||
/// @return Bitflags for b1-b4.
|
||||
uint8_t get_button_state();
|
||||
|
||||
/// @brief Gets the switch that was just flipped up (if any)
|
||||
/// @param switch_ an OUT variable for the switch that was flipped up (if any)
|
||||
/// @return true if there was a switch that was just flipped up
|
||||
bool get_switch_flipped_up(SwitchKey* switch_);
|
||||
/// @brief Gets the switch that was just flipped down (if any)
|
||||
/// @param switch_ an OUT variable for the switch that was flipped down (if any)
|
||||
/// @return true if there was a switch that was just flipped down
|
||||
bool get_switch_flipped_down(SwitchKey* switch_);
|
||||
/// @brief Gets the switch that was just flipped (if any)
|
||||
/// @param switch_ an OUT variable for the switch that was flipped (if any)
|
||||
/// @return true if there was a switch that was just flipped
|
||||
bool get_switch_flipped(SwitchKey* switch_);
|
||||
/// @brief Gets the raw state of the switches s1-s4 as bitflags.
|
||||
/// S1 is MSB and S4 is LSB.
|
||||
/// @return Bitflags for s1-s4.
|
||||
uint8_t get_switch_state();
|
||||
|
||||
/// @brief Gets the switch that was just touched (if any)
|
||||
/// @param switch_ an OUT variable for the switch that was touched (if any)
|
||||
/// @return true if there was a switch that was just touched
|
||||
bool get_switch_touch_pressed(SwitchKey* switch_);
|
||||
/// @brief Gets the switch that was just un-touched (if any)
|
||||
/// @param switch_ an OUT variable for the switch that was un-touched (if any)
|
||||
/// @return true if there was a switch that was just un-touched
|
||||
bool get_switch_touch_released(SwitchKey* switch_);
|
||||
/// @brief Gets the raw state of the touched switches s1-s4 as bitflags.
|
||||
/// S1 is MSB and S4 is LSB.
|
||||
/// @return Bitflags for the touched state of S1-S4.
|
||||
uint8_t get_switch_touch_state();
|
||||
|
||||
/// @brief Gets the state of the fingerprint sensor
|
||||
/// @return true if the fingerprint sensor is touched
|
||||
bool get_touch_state();
|
||||
/// @brief Gets whether or not the touch sensor was just pressed
|
||||
/// @return true if the touch sensor was just pressed
|
||||
bool get_touch_pressed();
|
||||
/// @brief Gets whether or not the touch sensor was just released
|
||||
/// @return true if the touch sensor was just released
|
||||
bool get_touch_released();
|
||||
|
||||
/// @brief A helper function for internal use.
|
||||
///
|
||||
/// Takes one key from the bitfield and sets the `kp` variable accordingly if the bitfield is not 0.
|
||||
/// @param kp Out. The keypad key to set.
|
||||
/// @param keypad_bitfield A pointer to the keypad bitfield to take a key from
|
||||
/// @return true if a key was taken from the bitfield
|
||||
bool take_key(KeypadKey* kp, uint16_t* keypad_bitfield);
|
||||
|
||||
// TODO: add touch sensor for switch
|
||||
|
||||
#endif /* BOTTOM_HALF_HPP */
|
||||
338
main/drivers/char_lcd.cpp
Normal file
338
main/drivers/char_lcd.cpp
Normal file
@ -0,0 +1,338 @@
|
||||
#include "char_lcd.h"
|
||||
|
||||
#include "./i2c_lcd_pcf8574.h"
|
||||
#include <esp_log.h>
|
||||
#include "state_tracking.h"
|
||||
#include <cstring>
|
||||
#include "power.h"
|
||||
#include "starcode.h"
|
||||
#include "game_info.h"
|
||||
|
||||
i2c_lcd_pcf8574_handle_t lcd;
|
||||
SemaphoreHandle_t lcd_mutex;
|
||||
|
||||
static volatile bool cursor_visible = false;
|
||||
static volatile uint8_t cursor_resting_row = 0;
|
||||
static volatile uint8_t cursor_resting_col = 0;
|
||||
|
||||
static volatile bool header_enabled = false;
|
||||
|
||||
static const char *TAG = "char_lcd";
|
||||
static const char* EMPTY_ROW = " ";
|
||||
|
||||
static char buf[65];
|
||||
|
||||
// TODO: move this to power.cpp
|
||||
static void monitor_battery_task(void* _arg) {
|
||||
(void) _arg;
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1'000));
|
||||
lcd_print_header_bat();
|
||||
}
|
||||
}
|
||||
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
if (strcmp(event, "LCD_CLEAR") == 0) {
|
||||
lcd_clear();
|
||||
}
|
||||
else if (strcmp(event, "LCD_SET_DISPLAY") == 0) {
|
||||
lcd_set_display(strcmp(arg, "true") == 0);
|
||||
}
|
||||
else if (strcmp(event, "LCD_CURSOR_VIS") == 0) {
|
||||
lcd_set_cursor_vis(strcmp(arg, "true") == 0);
|
||||
}
|
||||
else if (strcmp(event, "LCD_CURSOR_BLINK") == 0) {
|
||||
lcd_set_cursor_blink(strcmp(arg, "true") == 0);
|
||||
}
|
||||
else if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) {
|
||||
lcd_scroll_display_left();
|
||||
}
|
||||
else if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) {
|
||||
lcd_scroll_display_right();
|
||||
}
|
||||
else if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) {
|
||||
lcd_left_to_right();
|
||||
}
|
||||
else if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) {
|
||||
lcd_right_to_left();
|
||||
}
|
||||
else if (strcmp(event, "LCD_AUTOSCROLL") == 0) {
|
||||
lcd_set_autoscroll(strcmp(arg, "true") == 0);
|
||||
}
|
||||
else if (strcmp(event, "LCD_BACKLIGHT") == 0) {
|
||||
lcd_set_backlight(strcmp(arg, "true") == 0);
|
||||
}
|
||||
else if (strcmp(event, "LCD_CREATE_CHAR") == 0) {
|
||||
char* location_str = strtok(arg, ",");
|
||||
uint8_t location = atoi(location_str);
|
||||
|
||||
uint8_t charmap[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
char* str = strtok(NULL, ",");
|
||||
charmap[i] = atoi(str);
|
||||
}
|
||||
|
||||
lcd_create_char(location, charmap);
|
||||
}
|
||||
else if (strcmp(event, "LCD_PRINT") == 0) {
|
||||
char* str = strtok(arg, ",");
|
||||
uint8_t row = atoi(str);
|
||||
str = strtok(NULL, ",");
|
||||
uint8_t col = atoi(str);
|
||||
// get remaining part of string.
|
||||
str = strtok(NULL, "");
|
||||
|
||||
// TODO: handle \r and \n
|
||||
lcd_print(row, col, str);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_lcd() {
|
||||
ESP_LOGI(TAG, "Initializing LCD...");
|
||||
|
||||
lcd_mutex = xSemaphoreCreateMutex();
|
||||
assert(lcd_mutex != NULL);
|
||||
|
||||
lcd_init(&lcd, LCD_ADDR, CHAR_LCD_I2C_NUM);
|
||||
lcd_begin(&lcd, LCD_COLS, LCD_ROWS);
|
||||
|
||||
lcd_set_backlight_to(&lcd, 1);
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
xTaskCreate(monitor_battery_task, "bat_monitor", 1024*2, nullptr, 0, nullptr);
|
||||
|
||||
ESP_LOGI(TAG, "LCD initialized!");
|
||||
}
|
||||
|
||||
void lcd_clear(bool no_lock) {
|
||||
if (!header_enabled) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_clear(&lcd);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_CLEAR", NULL);
|
||||
}
|
||||
} else {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_print(1, 0, EMPTY_ROW, true);
|
||||
lcd_print(2, 0, EMPTY_ROW, true);
|
||||
lcd_print(3, 0, EMPTY_ROW, true);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_set_display(bool display, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
if (display) {
|
||||
lcd_display(&lcd);
|
||||
} else {
|
||||
lcd_no_display(&lcd);
|
||||
}
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_SET_DISPLAY", display ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_set_cursor_vis(bool cursor, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
if (cursor) {
|
||||
lcd_cursor(&lcd);
|
||||
} else {
|
||||
lcd_no_cursor(&lcd);
|
||||
}
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
cursor_visible = cursor;
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_CURSOR_VIS", cursor ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_set_cursor_blink(bool blink, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
if (blink) {
|
||||
lcd_blink(&lcd);
|
||||
} else {
|
||||
lcd_no_blink(&lcd);
|
||||
}
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_CURSOR_BLINK", blink ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_scroll_display_left(bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_scroll_display_left(&lcd);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_SCROLL_DISPLAY_LEFT", NULL);
|
||||
}
|
||||
}
|
||||
void lcd_scroll_display_right(bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_scroll_display_right(&lcd);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_SCROLL_DISPLAY_RIGHT", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_left_to_right(bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_left_to_right(&lcd);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_LEFT_TO_RIGHT", NULL);
|
||||
}
|
||||
}
|
||||
void lcd_right_to_left(bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_right_to_left(&lcd);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_RIGHT_TO_LEFT", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_set_autoscroll(bool autoscroll, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
if (autoscroll) {
|
||||
lcd_autoscroll(&lcd);
|
||||
} else {
|
||||
lcd_no_autoscroll(&lcd);
|
||||
}
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LCD_AUTOSCROLL", autoscroll ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_set_backlight(bool backlight, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_set_backlight_to(&lcd, backlight);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
sprintf(buf, "%d", backlight);
|
||||
event_occured("LCD_BACKLIGHT", backlight ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_create_char(uint8_t location, const uint8_t charmap[], bool no_lock) {
|
||||
if (location == 8) location = 0;
|
||||
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_create_char(&lcd, location, charmap);
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
snprintf(buf, sizeof(buf),
|
||||
"%d,%d,%d,%d,%d,%d,%d,%d,%d", location,
|
||||
charmap[0], charmap[1], charmap[2], charmap[3], charmap[4], charmap[5], charmap[6], charmap[7]
|
||||
);
|
||||
event_occured("LCD_CREATE_CHAR", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_print(uint8_t row, uint8_t col, const char* str, bool no_lock) {
|
||||
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_set_cursor(&lcd, col, row);
|
||||
lcd_print(&lcd, str);
|
||||
|
||||
if (cursor_visible) {
|
||||
lcd_set_cursor(&lcd, cursor_resting_col, cursor_resting_row);
|
||||
}
|
||||
|
||||
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
// TODO: handle \r and \n and others
|
||||
snprintf(buf, sizeof(buf), "%d,%d,%s", row, col, str);
|
||||
event_occured("LCD_PRINT", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void set_lcd_header_enabled(bool enable) {
|
||||
bool old_header_enabled = header_enabled;
|
||||
header_enabled = enable;
|
||||
|
||||
// update header in response to enabling/disabling the header
|
||||
if (enable && !old_header_enabled) {
|
||||
lcd_print_header();
|
||||
} else if (!enable && old_header_enabled) {
|
||||
lcd_print(0, 0, EMPTY_ROW);
|
||||
}
|
||||
}
|
||||
|
||||
bool lcd_header_enabled() {
|
||||
return header_enabled;
|
||||
}
|
||||
|
||||
void lcd_print_header() {
|
||||
lcd_print_header_star_code();
|
||||
lcd_print_header_step();
|
||||
lcd_print_header_bat();
|
||||
}
|
||||
|
||||
void lcd_do_splash() {
|
||||
const uint8_t custom_char[6][8] = {
|
||||
{ 0x01, 0x01, 0x02, 0x02, 0x07, 0x07, 0x0F, 0x0D },
|
||||
{ 0x10, 0x10, 0x18, 0x18, 0x1C, 0x0C, 0x0E, 0x06 },
|
||||
{ 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x07 },
|
||||
{ 0x19, 0x1B, 0x13, 0x17, 0x07, 0x0F, 0x0F, 0x1F },
|
||||
{ 0x13, 0x1B, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x1F },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x18, 0x1C, 0x1C },
|
||||
};
|
||||
|
||||
// TODO: make the lcd_lib somehow support the custom character 0 which would otherwise be a null terminator
|
||||
xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||
lcd_create_char(1, custom_char[0], true);
|
||||
lcd_create_char(2, custom_char[1], true);
|
||||
lcd_create_char(3, custom_char[2], true);
|
||||
lcd_create_char(4, custom_char[3], true);
|
||||
lcd_create_char(5, custom_char[4], true);
|
||||
lcd_create_char(6, custom_char[5], true);
|
||||
|
||||
lcd_print(1, 6, "\x01\x02Marino", true);
|
||||
lcd_print(2, 5, "\x03\x04\x05\x06""DEV", true);
|
||||
xSemaphoreGive(lcd_mutex);
|
||||
}
|
||||
|
||||
bool lcd_lock(uint32_t ticks_to_wait) {
|
||||
return xSemaphoreTake(lcd_mutex, ticks_to_wait);
|
||||
}
|
||||
|
||||
void lcd_unlock() {
|
||||
xSemaphoreGive(lcd_mutex);
|
||||
}
|
||||
|
||||
void lcd_set_cursor_resting_position(uint8_t row, uint8_t col) {
|
||||
cursor_resting_row = row;
|
||||
cursor_resting_col = col;
|
||||
}
|
||||
|
||||
void lcd_get_cursor_resting_position(uint8_t* row, uint8_t* col) {
|
||||
if (row) *row = cursor_resting_row;
|
||||
if (col) *col = cursor_resting_col;
|
||||
}
|
||||
|
||||
bool lcd_is_cursor_visible() {
|
||||
return cursor_visible;
|
||||
}
|
||||
96
main/drivers/char_lcd.h
Normal file
96
main/drivers/char_lcd.h
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef CHAR_LCD_H
|
||||
#define CHAR_LCD_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define CHAR_LCD_I2C_NUM I2C_NUM_0
|
||||
|
||||
#define LCD_ADDR 0x27
|
||||
#define LCD_ROWS 4
|
||||
#define LCD_COLS 20
|
||||
|
||||
/// @brief Initializes the 2004 Character LCD
|
||||
void init_lcd();
|
||||
|
||||
/// @brief Clear the LCD
|
||||
void lcd_clear(bool no_lock = false);
|
||||
|
||||
/// @brief Move cursor to home position
|
||||
void lcd_cursor_home(bool no_lock = false);
|
||||
|
||||
/// @brief Turn the display on/off
|
||||
void lcd_set_display(bool display, bool no_lock = false);
|
||||
|
||||
/// @brief Turn the cursor's visibility on/off
|
||||
void lcd_set_cursor_vis(bool cursor, bool no_lock = false);
|
||||
|
||||
/// @brief Turn blinking cursor on/off
|
||||
void lcd_set_cursor_blink(bool blink, bool no_lock = false);
|
||||
|
||||
/// @brief Scroll the display left
|
||||
void lcd_scroll_display_left(bool no_lock = false);
|
||||
/// @brief Scroll the display right
|
||||
void lcd_scroll_display_right(bool no_lock = false);
|
||||
|
||||
/// @brief Set the text to flows automatically left to right
|
||||
void lcd_left_to_right(bool no_lock = false);
|
||||
/// @brief Set the text to flows automatically right to left
|
||||
void lcd_right_to_left(bool no_lock = false);
|
||||
|
||||
/// @brief Turn on/off autoscroll
|
||||
void lcd_set_autoscroll(bool autoscroll, bool no_lock = false);
|
||||
|
||||
/// @brief Set backlight brightness
|
||||
void lcd_set_backlight(bool backlight, bool no_lock = false);
|
||||
|
||||
/// @brief Create a custom character. You get 8 custom characters.
|
||||
/// You can print custom characters by using escape characters in strings:
|
||||
/// use '\x01' - '\x07' for custom characters 1-7. Use '\x08' for custom char 0.
|
||||
void lcd_create_char(uint8_t location, const uint8_t charmap[], bool no_lock = false);
|
||||
|
||||
/// @brief Print a string to the LCD at a given pos.
|
||||
/// @param row the row the print the string at.
|
||||
/// @param col the column to print the string at.
|
||||
/// @param str the string to print.
|
||||
void lcd_print(uint8_t row, uint8_t col, const char* str, bool no_lock = false);
|
||||
|
||||
/// @brief Enables or disables the header on the LCD.
|
||||
/// @param enable `true` to enable the header, `false` to disable.
|
||||
void set_lcd_header_enabled(bool enable);
|
||||
|
||||
/// @brief Returns weather or not the lcd_header is enabled.
|
||||
/// @return `true` if the header is enabled, `false` otherwise.
|
||||
bool lcd_header_enabled();
|
||||
|
||||
/// @brief Prints the header in the LCD.
|
||||
void lcd_print_header();
|
||||
|
||||
/// @brief Prints the splash screen for the BLK_BOX.
|
||||
void lcd_do_splash();
|
||||
|
||||
/// @brief Locks the LCD to allow chaining multiple commands without interuptions.
|
||||
///
|
||||
/// Commands you call while you lock the LCD, you must call with the `no_lock` flag set to true.
|
||||
///
|
||||
/// Do not hold this lock for an extended period of time.
|
||||
/// @return `true` iff the lock was aquired.
|
||||
bool lcd_lock(uint32_t ticks_to_wait);
|
||||
|
||||
/// @brief Unlocks the LCD to give away the mutex access to it.
|
||||
void lcd_unlock();
|
||||
|
||||
/// @brief Set the resting position for the cursor
|
||||
/// @param row the row where the cursor should rest
|
||||
/// @param col the column where the cursor should rest
|
||||
void lcd_set_cursor_resting_position(uint8_t row, uint8_t col);
|
||||
|
||||
/// @brief Get the current resting position of the cursor
|
||||
/// @param row pointer to store the resting row
|
||||
/// @param col pointer to store the resting column
|
||||
void lcd_get_cursor_resting_position(uint8_t* row, uint8_t* col);
|
||||
|
||||
/// @brief Check if the cursor is currently visible
|
||||
/// @return true if cursor is visible, false otherwise
|
||||
bool lcd_is_cursor_visible();
|
||||
|
||||
#endif /* CHAR_LCD_H */
|
||||
467
main/drivers/esp_lcd_ili9488/esp_lcd_ili9488.c
Normal file
467
main/drivers/esp_lcd_ili9488/esp_lcd_ili9488.c
Normal file
@ -0,0 +1,467 @@
|
||||
// Addapted from:
|
||||
// https://github.com/atanisoft/esp_lcd_ili9488
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 atanisoft (github.com/atanisoft)
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_lcd_panel_interface.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <esp_lcd_panel_commands.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_rom_gpio.h>
|
||||
#include <esp_check.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
static const char *TAG = "ili9488";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t data[16];
|
||||
uint8_t data_bytes;
|
||||
} lcd_init_cmd_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
esp_lcd_panel_t base;
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
bool reset_level;
|
||||
int x_gap;
|
||||
int y_gap;
|
||||
uint8_t memory_access_control;
|
||||
uint8_t color_mode;
|
||||
size_t buffer_size;
|
||||
uint8_t *color_buffer;
|
||||
} ili9488_panel_t;
|
||||
|
||||
enum ili9488_constants
|
||||
{
|
||||
ILI9488_INTRFC_MODE_CTL = 0xB0,
|
||||
ILI9488_FRAME_RATE_NORMAL_CTL = 0xB1,
|
||||
ILI9488_INVERSION_CTL = 0xB4,
|
||||
ILI9488_FUNCTION_CTL = 0xB6,
|
||||
ILI9488_ENTRY_MODE_CTL = 0xB7,
|
||||
ILI9488_POWER_CTL_ONE = 0xC0,
|
||||
ILI9488_POWER_CTL_TWO = 0xC1,
|
||||
ILI9488_POWER_CTL_THREE = 0xC5,
|
||||
ILI9488_POSITIVE_GAMMA_CTL = 0xE0,
|
||||
ILI9488_NEGATIVE_GAMMA_CTL = 0xE1,
|
||||
ILI9488_ADJUST_CTL_THREE = 0xF7,
|
||||
|
||||
ILI9488_COLOR_MODE_16BIT = 0x55,
|
||||
ILI9488_COLOR_MODE_18BIT = 0x66,
|
||||
|
||||
ILI9488_INTERFACE_MODE_USE_SDO = 0x00,
|
||||
ILI9488_INTERFACE_MODE_IGNORE_SDO = 0x80,
|
||||
|
||||
ILI9488_IMAGE_FUNCTION_DISABLE_24BIT_DATA = 0x00,
|
||||
|
||||
ILI9488_WRITE_MODE_BCTRL_DD_ON = 0x28,
|
||||
ILI9488_FRAME_RATE_60HZ = 0xA0,
|
||||
|
||||
ILI9488_INIT_LENGTH_MASK = 0x1F,
|
||||
ILI9488_INIT_DONE_FLAG = 0xFF
|
||||
};
|
||||
|
||||
static esp_err_t panel_ili9488_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
|
||||
if (ili9488->reset_gpio_num >= 0)
|
||||
{
|
||||
gpio_reset_pin(ili9488->reset_gpio_num);
|
||||
}
|
||||
|
||||
if (ili9488->color_buffer != NULL)
|
||||
{
|
||||
heap_caps_free(ili9488->color_buffer);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "del ili9488 panel @%p", ili9488);
|
||||
free(ili9488);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
|
||||
if (ili9488->reset_gpio_num >= 0)
|
||||
{
|
||||
ESP_LOGI(TAG, "Setting GPIO:%d to %d", ili9488->reset_gpio_num,
|
||||
ili9488->reset_level);
|
||||
// perform hardware reset
|
||||
gpio_set_level(ili9488->reset_gpio_num, ili9488->reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ESP_LOGI(TAG, "Setting GPIO:%d to %d", ili9488->reset_gpio_num,
|
||||
!ili9488->reset_level);
|
||||
gpio_set_level(ili9488->reset_gpio_num, !ili9488->reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Sending SW_RESET to display");
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
|
||||
// ORIGINAL
|
||||
lcd_init_cmd_t ili9488_init[] =
|
||||
{
|
||||
#if CONFIG_USE_NEW_DISPLAY
|
||||
{ ILI9488_POSITIVE_GAMMA_CTL, { 0x00, 0x08, 0x0c, 0x02, 0x0e, 0x04, 0x30, 0x45, 0x47, 0x04, 0x0C, 0x0a, 0x2e, 0x34, 0x0F }, 15 },
|
||||
{ ILI9488_NEGATIVE_GAMMA_CTL, { 0x00, 0x11, 0x0d, 0x01, 0x0f, 0x05, 0x39, 0x36, 0x51, 0x06, 0x0f, 0x0d, 0x33, 0x37, 0x0F }, 15 },
|
||||
#else
|
||||
{ ILI9488_POSITIVE_GAMMA_CTL, { 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F }, 15 },
|
||||
{ ILI9488_NEGATIVE_GAMMA_CTL, { 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F }, 15 },
|
||||
#endif /* CONFIG_USE_NEW_DISPLAY */
|
||||
|
||||
{ ILI9488_POWER_CTL_ONE, { 0x17, 0x15 }, 2 },
|
||||
{ ILI9488_POWER_CTL_TWO, { 0x41 }, 1 },
|
||||
{ ILI9488_POWER_CTL_THREE, { 0x00, 0x12, 0x80 }, 3 },
|
||||
{ LCD_CMD_MADCTL, { ili9488->memory_access_control }, 1 },
|
||||
{ LCD_CMD_COLMOD, { ili9488->color_mode }, 1 },
|
||||
{ ILI9488_INTRFC_MODE_CTL, { ILI9488_INTERFACE_MODE_USE_SDO }, 1 },
|
||||
{ ILI9488_FRAME_RATE_NORMAL_CTL, { ILI9488_FRAME_RATE_60HZ }, 1 },
|
||||
{ ILI9488_INVERSION_CTL, { 0x02 }, 1 },
|
||||
{ ILI9488_FUNCTION_CTL, { 0x02, 0x02, 0x3B }, 3},
|
||||
{ ILI9488_ENTRY_MODE_CTL, { 0xC6 }, 1 },
|
||||
{ ILI9488_ADJUST_CTL_THREE, { 0xA9, 0x51, 0x2C, 0x02 }, 4 },
|
||||
{ LCD_CMD_NOP, { 0 }, ILI9488_INIT_DONE_FLAG },
|
||||
};
|
||||
|
||||
// WITH CONSTS INLINED:
|
||||
// lcd_init_cmd_t ili9488_init[] =
|
||||
// {
|
||||
// { 0xE0,
|
||||
// { 0x00, 0x03, 0x09, 0x08, 0x16,
|
||||
// 0x0A, 0x3F, 0x78, 0x4C, 0x09,
|
||||
// 0x0A, 0x08, 0x16, 0x1A, 0x0F },
|
||||
// 15
|
||||
// },
|
||||
// { 0xE1,
|
||||
// { 0x00, 0x16, 0x19, 0x03, 0x0F,
|
||||
// 0x05, 0x32, 0x45, 0x46, 0x04,
|
||||
// 0x0E, 0x0D, 0x35, 0x37, 0x0F},
|
||||
// 15
|
||||
// },
|
||||
// { 0xC0, { 0x17, 0x15 }, 2 },
|
||||
// { 0xC1, { 0x41 }, 1 },
|
||||
// { 0xC5, { 0x00, 0x12, 0x80 }, 3 },
|
||||
// { 0x36, { ili9488->memory_access_control }, 1 },
|
||||
// { 0x3A, { ili9488->color_mode }, 1 },
|
||||
// { 0xB0, { ILI9488_INTERFACE_MODE_USE_SDO }, 1 },
|
||||
// { 0xB1, { ILI9488_FRAME_RATE_60HZ }, 1 },
|
||||
// { 0xB4, { 0x02 }, 1 },
|
||||
// { 0xB6, { 0x02, 0x02, 0x3B }, 3},
|
||||
// { 0xB7, { 0xC6 }, 1 },
|
||||
// { 0xF7, { 0xA9, 0x51, 0x2C, 0x02 }, 4 },
|
||||
// { 0x00, { 0 }, ILI9488_INIT_DONE_FLAG },
|
||||
// };
|
||||
|
||||
// code from LCD manufacturer. (Not working):
|
||||
// lcd_init_cmd_t ili9488_init[] = {
|
||||
// { 0xF7, { 0xA9, 0x51, 0x2C, 0x82 }, 4 },
|
||||
// { 0x36, { 0x48 }, 1 },
|
||||
// { 0x3A, { 0x55 }, 1 },
|
||||
// { 0xB4, { 0x02 }, 1 },
|
||||
// { 0xB1, { 0xA0, 0x11 }, 2 },
|
||||
// { 0xC0, { 0x0F, 0x0F }, 2 },
|
||||
// { 0xC1, { 0x41 }, 1 },
|
||||
// { 0xC2, { 0x22 }, 1 },
|
||||
// { 0xB7, { 0xC6 }, 1 },
|
||||
// { 0xc5, { 0x00, 0x53, 0x80 }, 3 },
|
||||
// { 0xE0, { 0x00, 0x08, 0x0c, 0x02, 0x0e, 0x04, 0x30, 0x45, 0x47, 0x04, 0x0C, 0x0a, 0x2e, 0x34, 0x0F }, 15 },
|
||||
// { 0xE1, { 0x00, 0x11, 0x0d, 0x01, 0x0f, 0x05, 0x39, 0x36, 0x51, 0x06, 0x0f, 0x0d, 0x33, 0x37, 0x0F }, 15 },
|
||||
// { 0x21, { 0x00 }, 1 },
|
||||
// { 0x3A, { 0x55 }, 1 },
|
||||
// { LCD_CMD_NOP, { 0 }, ILI9488_INIT_DONE_FLAG },
|
||||
// };
|
||||
|
||||
ESP_LOGI(TAG, "Initializing ILI9488");
|
||||
int cmd = 0;
|
||||
while ( ili9488_init[cmd].data_bytes != ILI9488_INIT_DONE_FLAG )
|
||||
{
|
||||
ESP_LOGD(TAG, "Sending CMD: %02x, len: %d", ili9488_init[cmd].cmd,
|
||||
ili9488_init[cmd].data_bytes & ILI9488_INIT_LENGTH_MASK);
|
||||
esp_lcd_panel_io_tx_param(
|
||||
io, ili9488_init[cmd].cmd, ili9488_init[cmd].data,
|
||||
ili9488_init[cmd].data_bytes & ILI9488_INIT_LENGTH_MASK);
|
||||
cmd++;
|
||||
}
|
||||
|
||||
// Take the display out of sleep mode.
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// Turn on the display.
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_DISPON, NULL, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
ESP_LOGI(TAG, "Initialization complete");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#define SEND_COORDS(start, end, io, cmd) \
|
||||
esp_lcd_panel_io_tx_param(io, cmd, (uint8_t[]) { \
|
||||
(start >> 8) & 0xFF, \
|
||||
start & 0xFF, \
|
||||
((end - 1) >> 8) & 0xFF, \
|
||||
(end - 1) & 0xFF, \
|
||||
}, 4)
|
||||
|
||||
static esp_err_t panel_ili9488_draw_bitmap(
|
||||
esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
|
||||
const void *color_data)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
assert((x_start < x_end) && (y_start < y_end) &&
|
||||
"starting position must be smaller than end position");
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
|
||||
x_start += ili9488->x_gap;
|
||||
x_end += ili9488->x_gap;
|
||||
y_start += ili9488->y_gap;
|
||||
y_end += ili9488->y_gap;
|
||||
|
||||
size_t color_data_len = (x_end - x_start) * (y_end - y_start);
|
||||
|
||||
SEND_COORDS(x_start, x_end, io, LCD_CMD_CASET);
|
||||
SEND_COORDS(y_start, y_end, io, LCD_CMD_RASET);
|
||||
|
||||
// When the ILI9488 is used in 18-bit color mode we need to convert the
|
||||
// incoming color data from RGB565 (16-bit) to RGB666.
|
||||
//
|
||||
// NOTE: 16-bit color does not work via SPI interface :(
|
||||
if (ili9488->color_mode == ILI9488_COLOR_MODE_18BIT)
|
||||
{
|
||||
uint8_t *buf = ili9488->color_buffer;
|
||||
uint16_t *raw_color_data = (uint16_t *) color_data;
|
||||
for (uint32_t i = 0, pixel_index = 0; i < color_data_len; i++) {
|
||||
buf[pixel_index++] = (uint8_t) (((raw_color_data[i] & 0xF800) >> 8) |
|
||||
((raw_color_data[i] & 0x8000) >> 13));
|
||||
buf[pixel_index++] = (uint8_t) ((raw_color_data[i] & 0x07E0) >> 3);
|
||||
buf[pixel_index++] = (uint8_t) (((raw_color_data[i] & 0x001F) << 3) |
|
||||
((raw_color_data[i] & 0x0010) >> 2));
|
||||
}
|
||||
|
||||
esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, buf, color_data_len * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16-bit color we can transmit as-is to the display.
|
||||
esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, color_data_len * 2);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#undef SEND_COORDS
|
||||
|
||||
static esp_err_t panel_ili9488_invert_color(
|
||||
esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
|
||||
if (invert_color_data)
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_INVON, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_INVOFF, NULL, 0);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_mirror(
|
||||
esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
if (mirror_x)
|
||||
{
|
||||
ili9488->memory_access_control &= ~LCD_CMD_MX_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ili9488->memory_access_control |= LCD_CMD_MX_BIT;
|
||||
}
|
||||
if (mirror_y)
|
||||
{
|
||||
ili9488->memory_access_control |= LCD_CMD_MY_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ili9488->memory_access_control &= ~LCD_CMD_MY_BIT;
|
||||
}
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, &ili9488->memory_access_control, 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
if (swap_axes)
|
||||
{
|
||||
ili9488->memory_access_control |= LCD_CMD_MV_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ili9488->memory_access_control &= ~LCD_CMD_MV_BIT;
|
||||
}
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, &ili9488->memory_access_control, 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_set_gap(
|
||||
esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
ili9488->x_gap = x_gap;
|
||||
ili9488->y_gap = y_gap;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_ili9488_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = ili9488->io;
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// In ESP-IDF v4.x the API used false for "on" and true for "off"
|
||||
// invert the logic to be consistent with IDF v5.x.
|
||||
on_off = !on_off;
|
||||
#endif
|
||||
|
||||
if (on_off)
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_DISPON, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io, LCD_CMD_DISPOFF, NULL, 0);
|
||||
}
|
||||
|
||||
// give time for the ILI9488 to recover after an on/off command
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_new_panel_ili9488(
|
||||
const esp_lcd_panel_io_handle_t io,
|
||||
const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
const size_t buffer_size,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ili9488_panel_t *ili9488 = NULL;
|
||||
ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid argument");
|
||||
ili9488 = (ili9488_panel_t *)(calloc(1, sizeof(ili9488_panel_t)));
|
||||
ESP_GOTO_ON_FALSE(ili9488, ESP_ERR_NO_MEM, err, TAG, "no mem for ili9488 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0)
|
||||
{
|
||||
gpio_config_t cfg;
|
||||
memset(&cfg, 0, sizeof(gpio_config_t));
|
||||
esp_rom_gpio_pad_select_gpio(panel_dev_config->reset_gpio_num);
|
||||
cfg.pin_bit_mask = BIT64(panel_dev_config->reset_gpio_num);
|
||||
cfg.mode = GPIO_MODE_OUTPUT;
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&cfg), err, TAG,
|
||||
"configure GPIO for RESET line failed");
|
||||
}
|
||||
|
||||
if (panel_dev_config->bits_per_pixel == 16)
|
||||
{
|
||||
ili9488->color_mode = ILI9488_COLOR_MODE_16BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_GOTO_ON_FALSE(buffer_size > 0, ESP_ERR_INVALID_ARG, err, TAG,
|
||||
"Color conversion buffer size must be specified");
|
||||
ili9488->color_mode = ILI9488_COLOR_MODE_18BIT;
|
||||
|
||||
// Allocate DMA buffer for color conversions
|
||||
ili9488->color_buffer =
|
||||
(uint8_t *)heap_caps_malloc(buffer_size * 3, MALLOC_CAP_DMA);
|
||||
ESP_GOTO_ON_FALSE(ili9488->color_buffer, ESP_ERR_NO_MEM, err, TAG,
|
||||
"Failed to allocate DMA color conversion buffer");
|
||||
}
|
||||
|
||||
ili9488->memory_access_control = LCD_CMD_MX_BIT | LCD_CMD_BGR_BIT;
|
||||
switch (panel_dev_config->color_space)
|
||||
{
|
||||
case ESP_LCD_COLOR_SPACE_RGB:
|
||||
ESP_LOGI(TAG, "Configuring for RGB color order");
|
||||
ili9488->memory_access_control &= ~LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
case ESP_LCD_COLOR_SPACE_BGR:
|
||||
ESP_LOGI(TAG, "Configuring for BGR color order");
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG,
|
||||
"Unsupported color mode!");
|
||||
}
|
||||
|
||||
ili9488->io = io;
|
||||
ili9488->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
ili9488->reset_level = panel_dev_config->flags.reset_active_high;
|
||||
ili9488->base.del = panel_ili9488_del;
|
||||
ili9488->base.reset = panel_ili9488_reset;
|
||||
ili9488->base.init = panel_ili9488_init;
|
||||
ili9488->base.draw_bitmap = panel_ili9488_draw_bitmap;
|
||||
ili9488->base.invert_color = panel_ili9488_invert_color;
|
||||
ili9488->base.set_gap = panel_ili9488_set_gap;
|
||||
ili9488->base.mirror = panel_ili9488_mirror;
|
||||
ili9488->base.swap_xy = panel_ili9488_swap_xy;
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ili9488->base.disp_off = panel_ili9488_disp_on_off;
|
||||
#else
|
||||
ili9488->base.disp_on_off = panel_ili9488_disp_on_off;
|
||||
#endif
|
||||
*ret_panel = &(ili9488->base);
|
||||
ESP_LOGI(TAG, "new ili9488 panel @%p", ili9488);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (ili9488)
|
||||
{
|
||||
if (panel_dev_config->reset_gpio_num >= 0)
|
||||
{
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
if (ili9488->color_buffer != NULL)
|
||||
{
|
||||
heap_caps_free(ili9488->color_buffer);
|
||||
}
|
||||
free(ili9488);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
45
main/drivers/esp_lcd_ili9488/esp_lcd_ili9488.h
Normal file
45
main/drivers/esp_lcd_ili9488/esp_lcd_ili9488.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Addapted from:
|
||||
// https://github.com/atanisoft/esp_lcd_ili9488
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 atanisoft (github.com/atanisoft)
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create LCD panel for model ILI9488
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config general panel device configuration
|
||||
* @param[in] buffer_size size of buffer to allocate for color conversions.
|
||||
* @param[out] ret_panel Returned LCD panel handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_NO_MEM if out of memory
|
||||
* - ESP_OK on success
|
||||
*
|
||||
* NOTE: If you are using the SPI interface you *MUST* 18-bit color mode
|
||||
* in @param panel_dev_config field bits_per_pixel and @param buffer_size
|
||||
* must be provided.
|
||||
*
|
||||
* NOTE: For parallel IO (Intel 8080) interface 16-bit color mode should
|
||||
* be used and @param buffer_size will be ignored.
|
||||
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_ili9488(const esp_lcd_panel_io_handle_t io,
|
||||
const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
const size_t buffer_size,
|
||||
esp_lcd_panel_handle_t *ret_panel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
21
main/drivers/game_info.cpp
Normal file
21
main/drivers/game_info.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "game_info.h"
|
||||
#include "starcode.h"
|
||||
#include <stdio.h>
|
||||
#include "char_lcd.h"
|
||||
|
||||
static char game_state[GAME_STATE_MAX_LEN+2] = " MENU ";
|
||||
|
||||
void set_game_state(const char* new_state) {
|
||||
snprintf(game_state, sizeof(game_state), " %-5s", new_state);
|
||||
}
|
||||
|
||||
void reset_game_state() {
|
||||
set_game_state("");
|
||||
}
|
||||
|
||||
void lcd_print_header_step() {
|
||||
if (!lcd_header_enabled()) return;
|
||||
if (lcd_starcode_displaying_result()) return;
|
||||
|
||||
lcd_print(0, 10, game_state);
|
||||
}
|
||||
17
main/drivers/game_info.h
Normal file
17
main/drivers/game_info.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef GAME_INFO_H
|
||||
#define GAME_INFO_H
|
||||
|
||||
#define GAME_STATE_MAX_LEN 5
|
||||
|
||||
/// @brief Sets the game state, used for the header.
|
||||
///
|
||||
/// Must be <= 5 characters
|
||||
void set_game_state(const char* new_state);
|
||||
|
||||
/// @brief Resets the game state to be blank.
|
||||
void reset_game_state();
|
||||
|
||||
/// @brief Prints the game state section of the header to the char_lcd. (row 0, columns 11-15)
|
||||
void lcd_print_header_step();
|
||||
|
||||
#endif /* GAME_INFO_H */
|
||||
131
main/drivers/game_timer.cpp
Normal file
131
main/drivers/game_timer.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include "game_timer.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char* TAG = "game_timer";
|
||||
|
||||
static bool is_module_playing;
|
||||
static bool is_game_playing;
|
||||
|
||||
static bool game_time_count_up;
|
||||
|
||||
// in ms
|
||||
static uint32_t game_time_left;
|
||||
static uint32_t module_time_left;
|
||||
|
||||
static void game_timer_task(void *arg);
|
||||
static uint32_t sat_sub(uint32_t x, uint32_t y);
|
||||
|
||||
void init_game_timers() {
|
||||
ESP_LOGI(TAG, "Initializing game timers...");
|
||||
|
||||
xTaskCreate(game_timer_task, "game_timers", 4096, NULL, 10, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "Game timers initialized!");
|
||||
}
|
||||
|
||||
static void write_game_time(uint32_t millis) {
|
||||
if (millis > 60'000) {
|
||||
int disp_time = (millis / 60'000) * 100;
|
||||
disp_time = disp_time + ((millis % 60'000) / 1'000);
|
||||
set_game_sseg_num(disp_time, 2);
|
||||
} else {
|
||||
set_game_sseg_num(millis / 100, 1);
|
||||
}
|
||||
}
|
||||
static void write_module_time(uint32_t millis) {
|
||||
if (millis > 60'000) {
|
||||
int disp_time = (millis / 60'000) * 100;
|
||||
disp_time = disp_time + ((millis % 60'000) / 1'000);
|
||||
set_module_sseg_num(disp_time, 2);
|
||||
} else {
|
||||
set_module_sseg_num(millis / 100, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void start_game_timer() {
|
||||
is_game_playing = true;
|
||||
}
|
||||
|
||||
void stop_game_timer() {
|
||||
is_game_playing = false;
|
||||
}
|
||||
|
||||
void start_module_timer() {
|
||||
is_module_playing = true;
|
||||
}
|
||||
|
||||
void stop_module_timer() {
|
||||
is_module_playing = false;
|
||||
}
|
||||
|
||||
void set_game_time(int32_t new_time) {
|
||||
if (new_time > 0) {
|
||||
game_time_count_up = false;
|
||||
game_time_left = new_time;
|
||||
} else {
|
||||
game_time_count_up = true;
|
||||
game_time_left = -new_time;
|
||||
}
|
||||
write_game_time(game_time_left);
|
||||
}
|
||||
|
||||
int32_t get_game_time() {
|
||||
if (game_time_count_up) {
|
||||
return -((int32_t) game_time_left);
|
||||
} else {
|
||||
return ((int32_t) game_time_left);
|
||||
}
|
||||
}
|
||||
|
||||
void set_module_time(uint32_t new_time) {
|
||||
module_time_left = new_time;
|
||||
write_module_time(module_time_left);
|
||||
}
|
||||
|
||||
uint32_t get_module_time() {
|
||||
return module_time_left;
|
||||
}
|
||||
|
||||
/// Issues a time penalty to the game time
|
||||
void time_penalty(uint32_t penalty) {
|
||||
if (game_time_count_up) {
|
||||
game_time_left += penalty;
|
||||
} else {
|
||||
game_time_left = sat_sub(game_time_left, penalty);
|
||||
if (game_time_left == 0) {
|
||||
game_time_count_up = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void game_timer_task(void *arg) {
|
||||
TickType_t lastWakeTime = xTaskGetTickCount();
|
||||
|
||||
const uint32_t frequency = 100;
|
||||
while (1) {
|
||||
vTaskDelayUntil( &lastWakeTime, pdMS_TO_TICKS(frequency));
|
||||
if (is_game_playing) {
|
||||
if (game_time_count_up) {
|
||||
game_time_left += frequency;
|
||||
} else {
|
||||
game_time_left = sat_sub(game_time_left, frequency);
|
||||
if (game_time_left == 0) {
|
||||
game_time_count_up = true;
|
||||
}
|
||||
}
|
||||
write_game_time(game_time_left);
|
||||
}
|
||||
if (is_module_playing) {
|
||||
module_time_left = sat_sub(module_time_left, frequency);
|
||||
write_module_time(module_time_left);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static uint32_t sat_sub(uint32_t x, uint32_t y) {
|
||||
uint32_t res = x - y;
|
||||
res &= -(res <= x);
|
||||
return res;
|
||||
}
|
||||
33
main/drivers/game_timer.h
Normal file
33
main/drivers/game_timer.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef GAME_TIMER_H
|
||||
#define GAME_TIMER_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sseg.h"
|
||||
|
||||
/// Initializes the game and module timers.
|
||||
void init_game_timers();
|
||||
|
||||
/// Starts the game timer
|
||||
void start_game_timer();
|
||||
/// Stops the game timer
|
||||
void stop_game_timer();
|
||||
|
||||
/// Starts the module timer
|
||||
void start_module_timer();
|
||||
/// Stops the module timer
|
||||
void stop_module_timer();
|
||||
|
||||
/// Sets the game time in ms
|
||||
void set_game_time(int32_t new_time);
|
||||
/// Gets the current game time in ms
|
||||
int32_t get_game_time();
|
||||
|
||||
/// Gets the current game time in ms
|
||||
void time_penalty(uint32_t penalty);
|
||||
|
||||
/// Sets the module time in ms
|
||||
void set_module_time(uint32_t new_time);
|
||||
/// Gets the current module time in ms
|
||||
uint32_t get_module_time();
|
||||
|
||||
#endif /* GAME_TIMER_H */
|
||||
494
main/drivers/hwdata.cpp
Normal file
494
main/drivers/hwdata.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
#include "hwdata.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "bottom_half.h"
|
||||
#include "char_lcd.h"
|
||||
#include "../helper.h"
|
||||
#include "nvs.h"
|
||||
|
||||
static const char* TAG = "hwdata";
|
||||
|
||||
HWData::HWData()
|
||||
: compat_mode(true)
|
||||
{}
|
||||
|
||||
HWData::HWData(HWData1 data, bool compat_mode)
|
||||
: compat_mode(compat_mode),
|
||||
inner(data)
|
||||
{}
|
||||
|
||||
esp_err_t HWData::save(nvs_handle_t handle, bool force) {
|
||||
if (compat_mode && !force) {
|
||||
ESP_LOGW(TAG, "Not saving due to being in compatability mode.");
|
||||
return ESP_OK;
|
||||
}
|
||||
return inner.save(handle);
|
||||
}
|
||||
|
||||
HWData HWData::load(nvs_handle_t handle) {
|
||||
esp_err_t err;
|
||||
|
||||
uint16_t stored_version = 0;
|
||||
err = nvs_get_u16(handle, "version", &stored_version);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "No NVS data found! using defaults");
|
||||
return HWData();
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(err);
|
||||
ESP_LOGE(TAG, "Other esp error! using defaults");
|
||||
return HWData();
|
||||
}
|
||||
|
||||
HWData1 data;
|
||||
switch (stored_version) {
|
||||
case 0:
|
||||
ESP_LOGE(TAG, "HWData version was 0! using defaults");
|
||||
return HWData();
|
||||
case 1:
|
||||
data.load(handle);
|
||||
return HWData(data, false);
|
||||
default:
|
||||
ESP_LOGW(TAG, "Max currently supported version is %d, but saved version is %d. Loading version %d anyway!", CURRENT_HWDATA_VERSION, stored_version, CURRENT_HWDATA_VERSION);
|
||||
data.load(handle);
|
||||
return HWData(data, true);
|
||||
}
|
||||
}
|
||||
|
||||
HWData1::HWData1() {}
|
||||
|
||||
esp_err_t HWData1::save(nvs_handle_t handle) const {
|
||||
ESP_ERROR_CHECK(nvs_set_u16(handle, "version", 1));
|
||||
|
||||
// Serial number
|
||||
ESP_ERROR_CHECK(nvs_set_str(handle, "serial_num", serial_num.c_str()));
|
||||
|
||||
// Revisions
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_ctrl_maj", rev_ctrl_maj));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_ctrl_min", rev_ctrl_min));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_exp_maj", rev_exp_maj));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_exp_min", rev_exp_min));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_ft_maj", rev_ft_maj));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_ft_min", rev_ft_min));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_fb_maj", rev_fb_maj));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "rev_fb_min", rev_fb_min));
|
||||
|
||||
// Enums
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "sseg_color_t", static_cast<uint8_t>(sseg_color_t)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "sseg_color_b", static_cast<uint8_t>(sseg_color_b)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "lcd_color", static_cast<uint8_t>(lcd_color)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "button_type", static_cast<uint8_t>(button_type)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "tft_type", static_cast<uint8_t>(tft_type)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "bat_type", static_cast<uint8_t>(bat_type)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "shape1", static_cast<uint8_t>(shape1)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "shape2", static_cast<uint8_t>(shape2)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "shape3", static_cast<uint8_t>(shape3)));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "shape4", static_cast<uint8_t>(shape4)));
|
||||
|
||||
// Other fields
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "switch_pos", switch_pos));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_speaker", has_speaker));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_mic", has_mic));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_ir", has_ir));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_rfid", has_rfid));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_fp", has_fp));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_fp_hall", has_fp_hall));
|
||||
ESP_ERROR_CHECK(nvs_set_u8(handle, "has_close_hall", has_close_hall));
|
||||
|
||||
// Battery capacity
|
||||
ESP_ERROR_CHECK(nvs_set_u16(handle, "bat_cap", bat_cap));
|
||||
|
||||
return nvs_commit(handle);
|
||||
}
|
||||
|
||||
void HWData1::load(nvs_handle_t handle) {
|
||||
char buf[128];
|
||||
size_t required_size = sizeof(buf);
|
||||
esp_err_t err = nvs_get_str(handle, "serial_num", buf, &required_size);
|
||||
serial_num = (err == ESP_OK) ? std::string(buf) : "";
|
||||
|
||||
nvs_get_u8(handle, "rev_ctrl_maj", &rev_ctrl_maj);
|
||||
nvs_get_u8(handle, "rev_ctrl_min", &rev_ctrl_min);
|
||||
nvs_get_u8(handle, "rev_exp_maj", &rev_exp_maj);
|
||||
nvs_get_u8(handle, "rev_exp_min", &rev_exp_min);
|
||||
nvs_get_u8(handle, "rev_ft_maj", &rev_ft_maj);
|
||||
nvs_get_u8(handle, "rev_ft_min", &rev_ft_min);
|
||||
nvs_get_u8(handle, "rev_fb_maj", &rev_fb_maj);
|
||||
nvs_get_u8(handle, "rev_fb_min", &rev_fb_min);
|
||||
|
||||
uint8_t tmp;
|
||||
if (nvs_get_u8(handle, "sseg_color_t", &tmp) == ESP_OK) sseg_color_t = static_cast<SSegColor>(tmp);
|
||||
if (nvs_get_u8(handle, "sseg_color_b", &tmp) == ESP_OK) sseg_color_b = static_cast<SSegColor>(tmp);
|
||||
if (nvs_get_u8(handle, "lcd_color", &tmp) == ESP_OK) lcd_color = static_cast<LCDColor>(tmp);
|
||||
if (nvs_get_u8(handle, "button_type", &tmp) == ESP_OK) button_type = static_cast<ButtonType>(tmp);
|
||||
if (nvs_get_u8(handle, "tft_type", &tmp) == ESP_OK) tft_type = static_cast<TFTType>(tmp);
|
||||
if (nvs_get_u8(handle, "bat_type", &tmp) == ESP_OK) bat_type = static_cast<BatType>(tmp);
|
||||
nvs_get_u16(handle, "bat_cap", &bat_cap);
|
||||
|
||||
if (nvs_get_u8(handle, "shape1", &tmp) == ESP_OK) shape1 = static_cast<ShapeType>(tmp);
|
||||
if (nvs_get_u8(handle, "shape2", &tmp) == ESP_OK) shape2 = static_cast<ShapeType>(tmp);
|
||||
if (nvs_get_u8(handle, "shape3", &tmp) == ESP_OK) shape3 = static_cast<ShapeType>(tmp);
|
||||
if (nvs_get_u8(handle, "shape4", &tmp) == ESP_OK) shape4 = static_cast<ShapeType>(tmp);
|
||||
|
||||
nvs_get_u8(handle, "switch_pos", &switch_pos);
|
||||
nvs_get_u8(handle, "has_speaker", &tmp); has_speaker = tmp;
|
||||
nvs_get_u8(handle, "has_mic", &tmp); has_mic = tmp;
|
||||
nvs_get_u8(handle, "has_ir", &tmp); has_ir = tmp;
|
||||
nvs_get_u8(handle, "has_rfid", &tmp); has_rfid = tmp;
|
||||
nvs_get_u8(handle, "has_fp", &tmp); has_fp = tmp;
|
||||
nvs_get_u8(handle, "has_fp_hall", &tmp); has_fp_hall = tmp;
|
||||
nvs_get_u8(handle, "has_close_hall", &tmp); has_close_hall = tmp;
|
||||
}
|
||||
|
||||
static void handle_uint8(KeypadKey key, uint8_t& val) {
|
||||
char key_c = char_of_keypad_key(key);
|
||||
bool is_digit = std::isdigit(static_cast<unsigned char>(key_c));
|
||||
uint8_t digit = is_digit ? static_cast<uint8_t>(key_c - '0') : 0;
|
||||
|
||||
if (key == KeypadKey::star) {
|
||||
val = 0;
|
||||
} else if (is_digit) {
|
||||
uint16_t new_digit = ((uint16_t) val) * 10 + (uint16_t) digit;
|
||||
if (new_digit < 255) val = new_digit;
|
||||
}
|
||||
}
|
||||
static void handle_uint16(KeypadKey key, uint16_t& val) {
|
||||
char key_c = char_of_keypad_key(key);
|
||||
bool is_digit = std::isdigit(static_cast<unsigned char>(key_c));
|
||||
uint8_t digit = is_digit ? static_cast<uint8_t>(key_c - '0') : 0;
|
||||
|
||||
if (key == KeypadKey::star) {
|
||||
val = 0;
|
||||
} else if (is_digit) {
|
||||
uint32_t new_digit = ((uint32_t) val) * 10 + (uint32_t) digit;
|
||||
if (new_digit < 65535) val = new_digit;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_enum(ButtonKey key, uint8_t& val, uint8_t n_items) {
|
||||
if (key == ButtonKey::b1) {
|
||||
// dec
|
||||
val = (val + n_items - 1) % n_items;
|
||||
} else if (key == ButtonKey::b2) {
|
||||
// inc
|
||||
val = (val + 1) % n_items;
|
||||
}
|
||||
}
|
||||
|
||||
void hardware_config() {
|
||||
clean_bomb();
|
||||
uint8_t current_item = 0;
|
||||
const uint8_t n_items = 28;
|
||||
|
||||
HWData1& hwdata = get_hw_data().inner;
|
||||
|
||||
ButtonKey btn;
|
||||
KeypadKey key;
|
||||
bool dirty = true;
|
||||
while (true) {
|
||||
if (dirty) {
|
||||
// display
|
||||
char name[21];
|
||||
char value[21];
|
||||
|
||||
switch (current_item) {
|
||||
case 0: // serial_num
|
||||
snprintf(name, sizeof(name), "%-20s", "serial_num");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.serial_num.c_str());
|
||||
break;
|
||||
|
||||
case 1: // rev_ctrl_maj
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_ctrl_maj");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_ctrl_maj);
|
||||
break;
|
||||
|
||||
case 2: // rev_ctrl_min
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_ctrl_min");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_ctrl_min);
|
||||
break;
|
||||
|
||||
case 3: // rev_exp_maj
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_exp_maj");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_exp_maj);
|
||||
break;
|
||||
|
||||
case 4: // rev_exp_min
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_exp_min");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_exp_min);
|
||||
break;
|
||||
|
||||
case 5: // rev_ft_maj
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_ft_maj");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_ft_maj);
|
||||
break;
|
||||
|
||||
case 6: // rev_ft_min
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_ft_min");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_ft_min);
|
||||
break;
|
||||
|
||||
case 7: // rev_fb_maj
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_fb_maj");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_fb_maj);
|
||||
break;
|
||||
|
||||
case 8: // rev_fb_min
|
||||
snprintf(name, sizeof(name), "%-20s", "rev_fb_min");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.rev_fb_min);
|
||||
break;
|
||||
|
||||
case 9: // sseg_color_t
|
||||
snprintf(name, sizeof(name), "%-20s", "sseg_color_t");
|
||||
snprintf(value, sizeof(value), "%s", SSEG_COLOR_NAMES[static_cast<uint8_t>(hwdata.sseg_color_t)]);
|
||||
break;
|
||||
|
||||
case 10: // sseg_color_b
|
||||
snprintf(name, sizeof(name), "%-20s", "sseg_color_b");
|
||||
snprintf(value, sizeof(value), "%s", SSEG_COLOR_NAMES[static_cast<uint8_t>(hwdata.sseg_color_b)]);
|
||||
break;
|
||||
|
||||
case 11: // lcd_color
|
||||
snprintf(name, sizeof(name), "%-20s", "lcd_color");
|
||||
snprintf(value, sizeof(value), "%s", LCD_COLOR_NAMES[static_cast<uint8_t>(hwdata.lcd_color)]);
|
||||
break;
|
||||
|
||||
case 12: // switch_pos
|
||||
snprintf(name, sizeof(name), "%-20s", "switch_pos");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.switch_pos);
|
||||
break;
|
||||
|
||||
case 13: // button_type
|
||||
snprintf(name, sizeof(name), "%-20s", "button_type");
|
||||
snprintf(value, sizeof(value), "%s", BUTTON_TYPE_NAMES[static_cast<uint8_t>(hwdata.button_type)]);
|
||||
break;
|
||||
|
||||
case 14: // tft_type
|
||||
snprintf(name, sizeof(name), "%-20s", "tft_type");
|
||||
snprintf(value, sizeof(value), "%s", TFT_TYPE_NAMES[static_cast<uint8_t>(hwdata.tft_type)]);
|
||||
break;
|
||||
|
||||
case 15: // bat_type
|
||||
snprintf(name, sizeof(name), "%-20s", "bat_type");
|
||||
snprintf(value, sizeof(value), "%s", BAT_TYPE_NAMES[static_cast<uint8_t>(hwdata.bat_type)]);
|
||||
break;
|
||||
|
||||
case 16: // bat_cap
|
||||
snprintf(name, sizeof(name), "%-20s", "bat_cap");
|
||||
snprintf(value, sizeof(value), "%d", hwdata.bat_cap);
|
||||
break;
|
||||
|
||||
case 17: // shape1
|
||||
snprintf(name, sizeof(name), "%-20s", "shape1");
|
||||
snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast<uint8_t>(hwdata.shape1)]);
|
||||
break;
|
||||
|
||||
case 18: // shape2
|
||||
snprintf(name, sizeof(name), "%-20s", "shape2");
|
||||
snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast<uint8_t>(hwdata.shape2)]);
|
||||
break;
|
||||
|
||||
case 19: // shape3
|
||||
snprintf(name, sizeof(name), "%-20s", "shape3");
|
||||
snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast<uint8_t>(hwdata.shape3)]);
|
||||
break;
|
||||
|
||||
case 20: // shape4
|
||||
snprintf(name, sizeof(name), "%-20s", "shape4");
|
||||
snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast<uint8_t>(hwdata.shape4)]);
|
||||
break;
|
||||
|
||||
case 21: // has_speaker
|
||||
snprintf(name, sizeof(name), "%-20s", "has_speaker");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_speaker ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 22: // has_mic
|
||||
snprintf(name, sizeof(name), "%-20s", "has_mic");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_mic ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 23: // has_ir
|
||||
snprintf(name, sizeof(name), "%-20s", "has_ir");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_ir ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 24: // has_rfid
|
||||
snprintf(name, sizeof(name), "%-20s", "has_rfid");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_rfid ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 25: // has_fp
|
||||
snprintf(name, sizeof(name), "%-20s", "has_fp");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_fp ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 26: // has_fp_hall
|
||||
snprintf(name, sizeof(name), "%-20s", "has_fp_hall");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_fp_hall ? "true" : "false");
|
||||
break;
|
||||
|
||||
case 27: // has_close_hall
|
||||
snprintf(name, sizeof(name), "%-20s", "has_close_hall");
|
||||
snprintf(value, sizeof(value), "%s", hwdata.has_close_hall ? "true" : "false");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lcd_print(1, 0, name);
|
||||
lcd_print(2, 0, value);
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
if (get_button_pressed(&btn)) {
|
||||
dirty = true;
|
||||
switch (btn) {
|
||||
case ButtonKey::b3: // dec
|
||||
current_item = (current_item + n_items - 1) % n_items;
|
||||
break;
|
||||
case ButtonKey::b4: // inc
|
||||
current_item = (current_item + 1) % n_items;
|
||||
break;
|
||||
default:
|
||||
switch (current_item) {
|
||||
case 9: // sseg_color_t
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.sseg_color_t), SSEG_COLOR_COUNT);
|
||||
break;
|
||||
|
||||
case 10: // sseg_color_b
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.sseg_color_b), SSEG_COLOR_COUNT);
|
||||
break;
|
||||
|
||||
case 11: // lcd_color
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.lcd_color), LCD_COLOR_COUNT);
|
||||
break;
|
||||
|
||||
case 13: // button_type
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.button_type), BUTTON_TYPE_COUNT);
|
||||
break;
|
||||
|
||||
case 14: // tft_type
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.tft_type), TFT_TYPE_COUNT);
|
||||
break;
|
||||
|
||||
case 15: // bat_type
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.bat_type), BAT_TYPE_COUNT);
|
||||
break;
|
||||
|
||||
case 17: // shape1
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.shape1), SHAPE_TYPE_COUNT);
|
||||
break;
|
||||
case 18: // shape2
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.shape2), SHAPE_TYPE_COUNT);
|
||||
break;
|
||||
case 19: // shape3
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.shape3), SHAPE_TYPE_COUNT);
|
||||
break;
|
||||
case 20: // shape4
|
||||
handle_enum(btn, reinterpret_cast<uint8_t&>(hwdata.shape4), SHAPE_TYPE_COUNT);
|
||||
break;
|
||||
|
||||
case 21: // has_speaker
|
||||
hwdata.has_speaker = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 22: // has_mic
|
||||
hwdata.has_mic = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 23: // has_ir
|
||||
hwdata.has_ir = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 24: // has_rfid
|
||||
hwdata.has_rfid = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 25: // has_fp
|
||||
hwdata.has_fp = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 26: // has_fp_hall
|
||||
hwdata.has_fp_hall = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
case 27: // has_close_hall
|
||||
hwdata.has_close_hall = btn == ButtonKey::button_green;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (get_keypad_pressed(&key)) {
|
||||
dirty = true;
|
||||
if (key == KeypadKey::pound) {
|
||||
// TODO: ask the user to save
|
||||
return; // done
|
||||
}
|
||||
|
||||
char key_c = char_of_keypad_key(key);
|
||||
bool is_digit = std::isdigit(static_cast<unsigned char>(key_c));
|
||||
uint8_t digit = is_digit ? static_cast<uint8_t>(key_c - '0') : 0;
|
||||
|
||||
// update the current value
|
||||
switch (current_item) {
|
||||
case 0: // serial_num
|
||||
if (key == KeypadKey::star) {
|
||||
hwdata.serial_num.clear();
|
||||
} else {
|
||||
hwdata.serial_num.push_back(char_of_keypad_key(key));
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // rev_ctrl_maj
|
||||
handle_uint8(key, hwdata.rev_ctrl_maj);
|
||||
break;
|
||||
|
||||
case 2: // rev_ctrl_min
|
||||
handle_uint8(key, hwdata.rev_ctrl_min);
|
||||
break;
|
||||
|
||||
case 3: // rev_exp_maj
|
||||
handle_uint8(key, hwdata.rev_exp_maj);
|
||||
break;
|
||||
|
||||
case 4: // rev_exp_min
|
||||
handle_uint8(key, hwdata.rev_exp_min);
|
||||
break;
|
||||
|
||||
case 5: // rev_ft_maj
|
||||
handle_uint8(key, hwdata.rev_ft_maj);
|
||||
break;
|
||||
|
||||
case 6: // rev_ft_min
|
||||
handle_uint8(key, hwdata.rev_ft_min);
|
||||
break;
|
||||
|
||||
case 7: // rev_fb_maj
|
||||
handle_uint8(key, hwdata.rev_fb_maj);
|
||||
break;
|
||||
|
||||
case 8: // rev_fb_min
|
||||
handle_uint8(key, hwdata.rev_fb_min);
|
||||
break;
|
||||
|
||||
case 12: // switch_pos
|
||||
if (digit == 2 || digit == 3) hwdata.switch_pos = digit;
|
||||
break;
|
||||
|
||||
case 16: // bat_cap
|
||||
handle_uint16(key, hwdata.bat_cap);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
200
main/drivers/hwdata.h
Normal file
200
main/drivers/hwdata.h
Normal file
@ -0,0 +1,200 @@
|
||||
#ifndef HWDATA_H
|
||||
#define HWDATA_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include "esp_err.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#define CURRENT_HWDATA_VERSION 1
|
||||
#define CURRENT_HWDATA_STRUCT HWData1
|
||||
|
||||
#define SSEG_COLOR_COUNT 10
|
||||
static constexpr const char* SSEG_COLOR_NAMES[SSEG_COLOR_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"NONE",
|
||||
"OTHER",
|
||||
"RED",
|
||||
"ORANGE",
|
||||
"YELLOW",
|
||||
"GREEN",
|
||||
"BLUE",
|
||||
"PURPLE",
|
||||
"WHITE"
|
||||
};
|
||||
enum class SSegColor : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NONE = 1,
|
||||
OTHER = 2,
|
||||
RED = 3,
|
||||
ORANGE = 4,
|
||||
YELLOW = 5,
|
||||
GREEN = 6,
|
||||
BLUE = 7,
|
||||
PURPLE = 8,
|
||||
WHITE = 9,
|
||||
};
|
||||
|
||||
#define LCD_COLOR_COUNT 8
|
||||
static constexpr const char* LCD_COLOR_NAMES[LCD_COLOR_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"NONE",
|
||||
"OTHER",
|
||||
"BLACK_GREEN",
|
||||
"WHITE_BLUE",
|
||||
"BLACK_SKY",
|
||||
"BLACK_WHITE",
|
||||
"WHITE_BLACK"
|
||||
};
|
||||
enum class LCDColor : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NONE = 1,
|
||||
OTHER = 2,
|
||||
BLACK_GREEN = 3,
|
||||
WHITE_BLUE = 4,
|
||||
BLACK_SKY = 5,
|
||||
BLACK_WHITE = 6,
|
||||
WHITE_BLACK = 7,
|
||||
};
|
||||
|
||||
#define BUTTON_TYPE_COUNT 6
|
||||
static constexpr const char* BUTTON_TYPE_NAMES[BUTTON_TYPE_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"NONE",
|
||||
"OTHER",
|
||||
"WHITE",
|
||||
"BROWN",
|
||||
"RED"
|
||||
};
|
||||
enum class ButtonType : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NONE = 1,
|
||||
OTHER = 2,
|
||||
WHITE = 3,
|
||||
BROWN = 4,
|
||||
RED = 5,
|
||||
};
|
||||
|
||||
#define TFT_TYPE_COUNT 5
|
||||
static constexpr const char* TFT_TYPE_NAMES[TFT_TYPE_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"NONE",
|
||||
"OTHER",
|
||||
"EAST_RISING",
|
||||
"SHENZHEN"
|
||||
};
|
||||
enum class TFTType : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NONE = 1,
|
||||
OTHER = 2,
|
||||
EAST_RISING = 3,
|
||||
SHENZHEN = 4,
|
||||
};
|
||||
|
||||
#define BAT_TYPE_COUNT 5
|
||||
static constexpr const char* BAT_TYPE_NAMES[BAT_TYPE_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"NONE",
|
||||
"OTHER",
|
||||
"BAT_18650",
|
||||
"LIPO"
|
||||
};
|
||||
enum class BatType : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NONE = 1,
|
||||
OTHER = 2,
|
||||
BAT_18650 = 3,
|
||||
LIPO = 4,
|
||||
};
|
||||
|
||||
#define SHAPE_TYPE_COUNT 11
|
||||
static constexpr const char* SHAPE_TYPE_NAMES[SHAPE_TYPE_COUNT] = {
|
||||
"UNKNOWN",
|
||||
"OTHER",
|
||||
"CIRCLE",
|
||||
"SQUARE",
|
||||
"TRIANGLE",
|
||||
"X",
|
||||
"STAR",
|
||||
"SPADE",
|
||||
"DIAMOND",
|
||||
"CLUB",
|
||||
"HEART"
|
||||
};
|
||||
enum class ShapeType : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
OTHER = 1,
|
||||
CIRCLE = 2,
|
||||
SQUARE = 3,
|
||||
TRIANGLE = 4,
|
||||
X = 5,
|
||||
STAR = 6,
|
||||
SPADE = 7,
|
||||
DIAMOND = 8,
|
||||
CLUB = 9,
|
||||
HEART = 10,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Version 1 of HWData, kept constant for migrations
|
||||
struct HWData1 {
|
||||
std::string serial_num;
|
||||
uint8_t rev_ctrl_maj;
|
||||
uint8_t rev_ctrl_min;
|
||||
uint8_t rev_exp_maj;
|
||||
uint8_t rev_exp_min;
|
||||
uint8_t rev_ft_maj;
|
||||
uint8_t rev_ft_min;
|
||||
uint8_t rev_fb_maj;
|
||||
uint8_t rev_fb_min;
|
||||
|
||||
SSegColor sseg_color_t;
|
||||
SSegColor sseg_color_b;
|
||||
LCDColor lcd_color;
|
||||
uint8_t switch_pos;
|
||||
ButtonType button_type;
|
||||
TFTType tft_type;
|
||||
BatType bat_type;
|
||||
uint16_t bat_cap;
|
||||
|
||||
ShapeType shape1;
|
||||
ShapeType shape2;
|
||||
ShapeType shape3;
|
||||
ShapeType shape4;
|
||||
|
||||
bool has_speaker;
|
||||
bool has_mic;
|
||||
bool has_ir;
|
||||
bool has_rfid;
|
||||
bool has_fp;
|
||||
bool has_fp_hall;
|
||||
bool has_close_hall;
|
||||
|
||||
HWData1();
|
||||
|
||||
esp_err_t save(nvs_handle_t handle) const;
|
||||
void load(nvs_handle_t handle);
|
||||
|
||||
// Add migration method as necessary
|
||||
};
|
||||
|
||||
/// @brief The current version of HWData, to be stored
|
||||
struct HWData {
|
||||
/// @brief `true` if there is some issue in loading, and we are doing a "best effort" to be compatible
|
||||
/// We should make no writes to NVS if this is `true`.
|
||||
volatile bool compat_mode;
|
||||
HWData1 inner;
|
||||
|
||||
HWData();
|
||||
HWData(HWData1 data, bool compat_mode);
|
||||
|
||||
esp_err_t save(nvs_handle_t handle, bool force = false);
|
||||
static HWData load(nvs_handle_t handle);
|
||||
};
|
||||
|
||||
void hardware_config();
|
||||
|
||||
#endif /* HWDATA_H */
|
||||
36
main/drivers/i2c.cpp
Normal file
36
main/drivers/i2c.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "i2c.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
static const char *TAG = "i2c";
|
||||
|
||||
SemaphoreHandle_t main_i2c_mutex;
|
||||
|
||||
void init_i2c() {
|
||||
ESP_LOGI(TAG, "Initializing i2c...");
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = PIN_I2C_SDA,
|
||||
.scl_io_num = PIN_I2C_SCL,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master = {
|
||||
// TODO: 400k?
|
||||
.clk_speed = 100*1000,
|
||||
},
|
||||
.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2c_param_config(MAIN_I2C_BUS_NUM, &conf));
|
||||
ESP_ERROR_CHECK(i2c_driver_install(MAIN_I2C_BUS_NUM, conf.mode, 0, 0, 0));
|
||||
|
||||
main_i2c_mutex = xSemaphoreCreateMutex();
|
||||
assert(main_i2c_mutex != NULL);
|
||||
|
||||
ESP_LOGI(TAG, "i2c initialized!");
|
||||
}
|
||||
26
main/drivers/i2c.h
Normal file
26
main/drivers/i2c.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef I2C_H
|
||||
#define I2C_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define MAIN_I2C_BUS_NUM I2C_NUM_0
|
||||
|
||||
#define PIN_I2C_SDA GPIO_NUM_7
|
||||
#define PIN_I2C_SCL GPIO_NUM_15
|
||||
|
||||
/// The mutex for accessing `I2C_NUM_0`.
|
||||
extern SemaphoreHandle_t main_i2c_mutex;
|
||||
|
||||
/// @brief Initializes `I2C_NUM_0`.
|
||||
///
|
||||
/// This is hooked up the to:
|
||||
/// - The bottom half
|
||||
/// - The char lcd
|
||||
/// - The power board
|
||||
/// - The MPU6050
|
||||
/// - The PERH port
|
||||
/// - The Capacitive Touch Panel
|
||||
void init_i2c();
|
||||
|
||||
#endif /* I2C_H */
|
||||
366
main/drivers/i2c_lcd_pcf8574.c
Normal file
366
main/drivers/i2c_lcd_pcf8574.c
Normal file
@ -0,0 +1,366 @@
|
||||
/// \file i2c_lcd_pcf8574.c
|
||||
/// \brief Liquid Crystal display driver with PCF8574 adapter for esp-idf
|
||||
///
|
||||
/// \author Femi Olugbon, https://iamflinks.github.io
|
||||
/// \copyright Copyright (c) 2024 by Femi Olugbon
|
||||
///
|
||||
/// ChangeLog see: i2c_lcd_pcf8574.h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "i2c_lcd_pcf8574.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "i2c.h"
|
||||
|
||||
#define TAG "I2C_LCD_PCF8574"
|
||||
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||
|
||||
// private functions
|
||||
static void lcd_send(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value, bool is_data);
|
||||
static void lcd_write_nibble(i2c_lcd_pcf8574_handle_t* lcd, uint8_t half_byte, bool is_data, i2c_cmd_handle_t cmd);
|
||||
static void lcd_write_i2c(i2c_lcd_pcf8574_handle_t* lcd, uint8_t data, bool is_data, bool enable);
|
||||
|
||||
void lcd_init(i2c_lcd_pcf8574_handle_t* lcd, uint8_t i2c_addr, i2c_port_t i2c_port) {
|
||||
lcd->i2c_addr = i2c_addr;
|
||||
lcd->i2c_port = i2c_port;
|
||||
lcd->backlight = 0;
|
||||
lcd->entrymode = 0x02; // Init the LCD with an internal reset
|
||||
lcd->displaycontrol = 0x04;
|
||||
lcd->rs_mask = 0x01;
|
||||
lcd->rw_mask = 0x00;
|
||||
lcd->enable_mask = 0x04;
|
||||
lcd->data_mask[0] = 0x10;
|
||||
lcd->data_mask[1] = 0x20;
|
||||
lcd->data_mask[2] = 0x40;
|
||||
lcd->data_mask[3] = 0x80;
|
||||
lcd->backlight_mask = 0x08;
|
||||
}
|
||||
|
||||
void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows) {
|
||||
|
||||
// Ensure the cols and rows stay within max limit
|
||||
lcd->cols = (cols > 80) ? 80 : cols;
|
||||
lcd->lines = (rows > 4) ? 4 : rows;
|
||||
|
||||
lcd->row_offsets[0] = 0x00;
|
||||
lcd->row_offsets[1] = 0x40;
|
||||
lcd->row_offsets[2] = 0x00 + cols;
|
||||
lcd->row_offsets[3] = 0x40 + cols;
|
||||
|
||||
// Initialize the LCD
|
||||
lcd_write_i2c(lcd, 0x00, false, false);
|
||||
esp_rom_delay_us(50000);
|
||||
|
||||
// This follows after the reset mode
|
||||
lcd->displaycontrol = 0x04;
|
||||
lcd->entrymode = 0x02;
|
||||
|
||||
// The following are the reset sequence: Please see "Initialization instruction in the PCF8574 datasheet."
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
// We left-shift the device addres and add the read/write command
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
|
||||
lcd_write_nibble(lcd, 0x03, false, cmd);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
esp_rom_delay_us(4500);
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
lcd_write_nibble(lcd, 0x03, false, cmd);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
esp_rom_delay_us(200);
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
lcd_write_nibble(lcd, 0x03, false, cmd);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
esp_rom_delay_us(200);
|
||||
|
||||
// Set the data interface to 4-bit interface (PCF8574 uses 4-bit interface)
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
lcd_write_nibble(lcd, 0x02, false, cmd);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
|
||||
// Instruction: function set = 0x20
|
||||
lcd_send(lcd, 0x20 | (rows > 1 ? 0x08 : 0x00), false);
|
||||
|
||||
// Set the display parameters (turn on display, clear, and set left to right)
|
||||
lcd_display(lcd);
|
||||
lcd_clear(lcd);
|
||||
lcd_left_to_right(lcd);
|
||||
} // lcd_begin()
|
||||
|
||||
// Clear the display content
|
||||
void lcd_clear(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Clear display = 0x01
|
||||
lcd_send(lcd, 0x01, false);
|
||||
// Clearing the display takes a while: takes approx. 1.5ms
|
||||
esp_rom_delay_us(2000);
|
||||
} // lcd_clear()
|
||||
|
||||
// Set the display to home
|
||||
void lcd_home(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Return home = 0x02
|
||||
lcd_send(lcd, 0x02, false);
|
||||
// Same as clearing the display: takes approx. 1.5ms
|
||||
esp_rom_delay_us(2000);
|
||||
} // lcd_home()
|
||||
|
||||
// Set the cursor to a new position.
|
||||
void lcd_set_cursor(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row) {
|
||||
// Check the display boundaries
|
||||
if (row >= lcd->lines) {
|
||||
row = lcd->lines - 1;
|
||||
}
|
||||
if (col >= lcd->cols) {
|
||||
col = lcd->cols - 1;
|
||||
}
|
||||
// Instruction: Set DDRAM address = 080
|
||||
lcd_send(lcd, 0x80 | (lcd->row_offsets[row] + col), false);
|
||||
} // lcd_set_cursor()
|
||||
|
||||
// Turn off the display: fast operation
|
||||
void lcd_no_display(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Display on/off control = 0x04
|
||||
lcd->displaycontrol &= ~0x04;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_no_display()
|
||||
|
||||
// Turn on the display: fast operation
|
||||
void lcd_display(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Display on/off control = 0x04
|
||||
lcd->displaycontrol |= 0x04;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_display()
|
||||
|
||||
// Turn on the cursor
|
||||
void lcd_cursor(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Cursor on/off control = 0x02
|
||||
lcd->displaycontrol |= 0x02;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_cursor()
|
||||
|
||||
// Turn off the cursor
|
||||
void lcd_no_cursor(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Cursor on/off control = 0x02
|
||||
lcd->displaycontrol &= ~0x02;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_no_cursor()
|
||||
|
||||
// Turn on the blinking
|
||||
void lcd_blink(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Blink on/off control = 0x01
|
||||
lcd->displaycontrol |= 0x01;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_blink()
|
||||
|
||||
// Turn off the blinking
|
||||
void lcd_no_blink(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Display Control: Blink on/off control = 0x01
|
||||
lcd->displaycontrol &= ~0x01;
|
||||
// Instruction: Display mode: 0x08
|
||||
lcd_send(lcd, 0x08 | lcd->displaycontrol, false);
|
||||
} // lcd_no_blink()
|
||||
|
||||
// This command will scroll the display left by one step without changing the RAM
|
||||
void lcd_scroll_display_left(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Cursor or display shift - 0x10
|
||||
// Instruction: Display mode: 0x08
|
||||
// Control: Left shift control = 0x00
|
||||
// 0x10 | 0x08 | 0x00 = 0x18
|
||||
lcd_send(lcd, 0x18, false);
|
||||
} // lcd_scroll_display_left()
|
||||
|
||||
// This command will scroll the display right by one step without changing the RAM
|
||||
void lcd_scroll_display_right(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Cursor or display shift - 0x10
|
||||
// Instruction: Display mode: 0x08
|
||||
// Control: Left shift control = 0x04
|
||||
// 0x10 | 0x08 | 0x04 = 0x1C
|
||||
lcd_send(lcd, 0x1C, false);
|
||||
} // lcd_scroll_display_right()
|
||||
|
||||
// Controlling the entry mode: This is for text that flows left to right
|
||||
void lcd_left_to_right(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Entry mode set, set increment/decrement = 0x02
|
||||
lcd->entrymode |= 0x02;
|
||||
lcd_send(lcd, 0x04 | lcd->entrymode, false);
|
||||
} // lcd_left_to_right()
|
||||
|
||||
// Controlling the entry mode: This is for text that flows right to left
|
||||
void lcd_right_to_left(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Entry mode set, clear increment/decrement = 0x02
|
||||
lcd->entrymode &= ~0x02;
|
||||
lcd_send(lcd, 0x04 | lcd->entrymode, false);
|
||||
} // lcd_right_to_left()
|
||||
|
||||
// This will justify the text to the right from the cursor
|
||||
void lcd_autoscroll(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Entry mode set, set shift = 0x01
|
||||
lcd->entrymode |= 0x01;
|
||||
lcd_send(lcd, 0x04 | lcd->entrymode, false);
|
||||
} // lcd_autoscroll()
|
||||
|
||||
// This will justify the text to the left from the cursor
|
||||
void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd) {
|
||||
// Instruction: Entry mode set, clear shift = 0x01
|
||||
lcd->entrymode &= ~0x01;
|
||||
lcd_send(lcd, 0x04 | lcd->entrymode, false);
|
||||
} // lcd_no_autoscroll()
|
||||
|
||||
// Setting the backlight: It can only be turn on or off.
|
||||
// Current backlight value is saved in the i2c_lcd_pcf8574_handle_t struct for further data transfers
|
||||
void lcd_set_backlight_to(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness) {
|
||||
// Place the backlight value in the lcd struct
|
||||
lcd->backlight = brightness;
|
||||
// Send no data
|
||||
lcd_write_i2c(lcd, 0x00, true, false);
|
||||
} // lcd_set_backlight()
|
||||
|
||||
// Custom character creation: allows us to create up to 8 custom characters in the CGRAM locations
|
||||
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]) {
|
||||
location &= 0x7; // Only 8 locations are available
|
||||
// Set the CGRAM address
|
||||
lcd_send(lcd, 0x40 | (location << 3), false);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
lcd_write(lcd, charmap[i]);
|
||||
}
|
||||
} // lcd_create_char()
|
||||
|
||||
// Write a byte to the LCD
|
||||
void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value) {
|
||||
lcd_send(lcd, value, true);
|
||||
} // lcd_write()
|
||||
|
||||
// Print characters to the LCD: cursor set or clear instruction must preceded this instruction, or it will write on the current text.
|
||||
void lcd_print(i2c_lcd_pcf8574_handle_t* lcd, const char* str) {
|
||||
while (*str) {
|
||||
if (*str == '\x08') {
|
||||
lcd_write(lcd, '\x00');
|
||||
str++;
|
||||
} else {
|
||||
lcd_write(lcd, *str++);
|
||||
}
|
||||
}
|
||||
} // lcd_print()
|
||||
|
||||
// Additional function to print numbers as formatted string
|
||||
void lcd_print_number(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row, uint8_t buf_len, const char *str, ...) {
|
||||
// Ensure the buffer length is greater than zero
|
||||
if (buf_len == 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Buffer length must be greater than 0");
|
||||
}
|
||||
|
||||
// Create a buffer to hold the characters
|
||||
char buffer[buf_len];
|
||||
|
||||
va_list args;
|
||||
va_start(args, str);
|
||||
|
||||
int chars_written = vsniprintf(buffer, buf_len, str, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (chars_written < 0) {
|
||||
ESP_LOGE(TAG, "Encoding error in vsnprintf");
|
||||
}
|
||||
|
||||
if ((size_t)chars_written >= buf_len) {
|
||||
ESP_LOGW(TAG, "Buffer overflow: %d characters needed, but only %d available", chars_written + 1, buf_len);
|
||||
}
|
||||
|
||||
lcd_set_cursor(lcd, col, row);
|
||||
lcd_print(lcd, buffer);
|
||||
|
||||
} // lcd_print_number()
|
||||
|
||||
|
||||
// Private functions: derived from the esp32 i2c_master driver
|
||||
|
||||
static void lcd_send(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value, bool is_data) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
lcd_write_nibble(lcd, (value >> 4 & 0x0F), is_data, cmd);
|
||||
lcd_write_nibble(lcd, (value & 0x0F), is_data, cmd);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send data to LCD: %s", esp_err_to_name(ret));
|
||||
}
|
||||
} // lcd_send()
|
||||
|
||||
// Write a nibble / half byte with ACK
|
||||
static void lcd_write_nibble(i2c_lcd_pcf8574_handle_t* lcd, uint8_t half_byte, bool is_data, i2c_cmd_handle_t cmd) {
|
||||
|
||||
// Map the data to the given pin connections
|
||||
uint8_t data = is_data ? lcd->rs_mask : 0;
|
||||
|
||||
// Don't use rw_mask here
|
||||
if (lcd->backlight > 0) {
|
||||
data |= lcd->backlight_mask;
|
||||
}
|
||||
// Allow arbitrary pin configuration
|
||||
if (half_byte & 0x01) data |= lcd->data_mask[0];
|
||||
if (half_byte & 0x02) data |= lcd->data_mask[1];
|
||||
if (half_byte & 0x04) data |= lcd->data_mask[2];
|
||||
if (half_byte & 0x08) data |= lcd->data_mask[3];
|
||||
|
||||
i2c_master_write_byte(cmd, data | lcd->enable_mask, true);
|
||||
i2c_master_write_byte(cmd, data, true);
|
||||
} // lcd_write_nibble()
|
||||
|
||||
// Private function to change the PCF8574 pins to the given value.
|
||||
static void lcd_write_i2c(i2c_lcd_pcf8574_handle_t* lcd, uint8_t data, bool is_data, bool enable) {
|
||||
if (is_data) {
|
||||
data |= lcd->rs_mask;
|
||||
}
|
||||
if (enable) {
|
||||
data |= lcd->enable_mask;
|
||||
}
|
||||
if (lcd->backlight > 0) {
|
||||
data |= lcd->backlight_mask;
|
||||
}
|
||||
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_write_byte(cmd, data, true);
|
||||
i2c_master_stop(cmd);
|
||||
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||
esp_err_t ret = i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||
xSemaphoreGive(main_i2c_mutex);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write to LCD: %s", esp_err_to_name(ret));
|
||||
}
|
||||
} // lcd_write_i2c()
|
||||
112
main/drivers/i2c_lcd_pcf8574.h
Normal file
112
main/drivers/i2c_lcd_pcf8574.h
Normal file
@ -0,0 +1,112 @@
|
||||
/// \file i2c_lcd_pcf8574.h
|
||||
/// \brief Liquid Crystal display driver with PCF8574 adapter for esp-idf
|
||||
///
|
||||
/// \author Femi Olugbon, https://iamflinks.github.io
|
||||
/// \copyright Copyright (c) 2024 by Femi Olugbon
|
||||
///
|
||||
/// The library work is lincensed under a BSD style license.
|
||||
///
|
||||
/// \details
|
||||
/// This library can drive a LCD based on the Hitachi's HD44790 display chip that is wired through a PCF8574 I2C converter. It uses the esp-idf i2c_driver component for communication. The library was adapted from the LiquidCrystal_PCF8574 (Mathias Hertel) and LiquidCrystal_I2C Arduino libraries.
|
||||
///
|
||||
///
|
||||
/// ChangeLog:
|
||||
/// ===========
|
||||
/// * 07/22/2024 --> Created
|
||||
/// * 07/23/2024 --> Added number printing functionality
|
||||
///
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef I2C_LCD_PCF8574_H
|
||||
#define I2C_LCD_PCF8574_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2c_master.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LCD_MAX_ROWS 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t i2c_addr;
|
||||
uint8_t backlight;
|
||||
uint8_t cols;
|
||||
uint8_t lines;
|
||||
uint8_t entrymode;
|
||||
uint8_t displaycontrol;
|
||||
uint8_t row_offsets[LCD_MAX_ROWS];
|
||||
uint8_t rs_mask;
|
||||
uint8_t rw_mask;
|
||||
uint8_t enable_mask;
|
||||
uint8_t backlight_mask;
|
||||
uint8_t data_mask[4];
|
||||
i2c_port_t i2c_port;
|
||||
} i2c_lcd_pcf8574_handle_t;
|
||||
|
||||
|
||||
// Initialize the LCD
|
||||
void lcd_init(i2c_lcd_pcf8574_handle_t* lcd, uint8_t i2c_addr, i2c_port_t i2c_port);
|
||||
|
||||
// Begin using the LCD
|
||||
void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows);
|
||||
|
||||
// Clear the LCD
|
||||
void lcd_clear(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Move cursor to home position
|
||||
void lcd_home(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Set cursor position
|
||||
void lcd_set_cursor(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row);
|
||||
|
||||
// Turn the display on/off
|
||||
void lcd_no_display(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_display(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Turn the cursor on/off
|
||||
void lcd_cursor(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_no_cursor(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Turn blinking cursor on/off
|
||||
void lcd_blink(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_no_blink(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Scroll the display
|
||||
void lcd_scroll_display_left(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_scroll_display_right(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Set the direction for text that flows automatically
|
||||
void lcd_left_to_right(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_right_to_left(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Turn on/off autoscroll
|
||||
void lcd_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
|
||||
|
||||
// Set backlight brightness
|
||||
void lcd_set_backlight_to(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness);
|
||||
|
||||
// Create a custom character
|
||||
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]);
|
||||
|
||||
// Write a character to the LCD
|
||||
void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value);
|
||||
|
||||
// Print a string to the LCD
|
||||
void lcd_print(i2c_lcd_pcf8574_handle_t* lcd, const char* str);
|
||||
|
||||
void lcd_print_number(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row, uint8_t buf_len, const char *str, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // C++ extern
|
||||
|
||||
#endif // I2C_LCD_PCF8574_H
|
||||
83
main/drivers/leds.cpp
Normal file
83
main/drivers/leds.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "leds.h"
|
||||
#include "led_strip.h"
|
||||
#include <esp_log.h>
|
||||
#include "state_tracking.h"
|
||||
#include <cstring>
|
||||
|
||||
static const char* TAG = "leds";
|
||||
|
||||
static led_strip_handle_t leds;
|
||||
|
||||
// TODO: rename these to playback_handler
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
if (strcmp(event, "LED_SET") == 0) {
|
||||
uint32_t led = atoi(strtok(arg, ","));
|
||||
uint32_t color = atoi(strtok(NULL, ","));
|
||||
led_set(led, color);
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "LED_FLUSH") == 0) {
|
||||
leds_flush();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "LED_CLR") == 0) {
|
||||
leds_clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_leds() {
|
||||
ESP_LOGI(TAG, "Initializing LEDs...");
|
||||
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = NEOPIXEL_PIN,
|
||||
.max_leds = LED_COUNT,
|
||||
.led_pixel_format = LED_PIXEL_FORMAT_GRB,
|
||||
.led_model = LED_MODEL_WS2812
|
||||
};
|
||||
|
||||
led_strip_rmt_config_t rmt_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = LED_STRIP_RMT_RES_HZ,
|
||||
};
|
||||
rmt_config.flags.with_dma = false;
|
||||
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &leds));
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
ESP_LOGI(TAG, "LEDs initialized!");
|
||||
}
|
||||
|
||||
void led_set(uint32_t led, uint8_t r, uint8_t g, uint8_t b) {
|
||||
led_strip_set_pixel(leds, led, r, g, b);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[32];
|
||||
uint32_t color = (r << 16) | (g << 8) | b;
|
||||
sprintf(buf, "%ld,%ld", led, color);
|
||||
event_occured("LED_SET", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void led_set(uint32_t led, uint32_t color) {
|
||||
led_set(led, (color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
|
||||
}
|
||||
|
||||
void leds_flush() {
|
||||
led_strip_refresh(leds);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LED_FLUSH", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void leds_clear() {
|
||||
led_strip_clear(leds);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("LED_CLR", NULL);
|
||||
}
|
||||
}
|
||||
74
main/drivers/leds.h
Normal file
74
main/drivers/leds.h
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef LEDS_H
|
||||
#define LEDS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LED_COUNT 21
|
||||
#define NEOPIXEL_PIN GPIO_NUM_0
|
||||
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||
|
||||
enum LEDColor: uint32_t {
|
||||
LED_COLOR_OFF = 0x00'00'00,
|
||||
LED_COLOR_RED = 0x17'00'00,
|
||||
LED_COLOR_RED_STRONG = 0xFF'00'00,
|
||||
LED_COLOR_ORANGE = 0x17'02'00,
|
||||
LED_COLOR_ORANGE_STRONG = 0xFF'20'00,
|
||||
LED_COLOR_YELLOW = 0x07'07'00,
|
||||
LED_COLOR_YELLOW_STRONG = 0xFF'FF'00,
|
||||
LED_COLOR_GREEN = 0x00'07'00,
|
||||
LED_COLOR_GREEN_STRONG = 0x00'FF'00,
|
||||
LED_COLOR_BLUE = 0x00'00'17,
|
||||
LED_COLOR_BLUE_STRONG = 0x00'00'FF,
|
||||
LED_COLOR_PINK = 0x10'00'04,
|
||||
LED_COLOR_PINK_STRONG = 0xFF'00'80,
|
||||
LED_COLOR_WHITE = 0x04'04'04,
|
||||
LED_COLOR_WHITE_STRONG = 0xFF'FF'FF,
|
||||
};
|
||||
|
||||
// TODO: sepperate the indicator leds from the shape display.
|
||||
enum IndicatorLED {
|
||||
LED_SHAPE1 = 0u,
|
||||
LED_SHAPE2 = 1u,
|
||||
LED_SHAPE3 = 2u,
|
||||
LED_SHAPE4 = 3u,
|
||||
LED_MODULE_SSEG = 4u,
|
||||
LED_GAME_SSEG = 5u,
|
||||
LED_TFT = 6u,
|
||||
LED_MIC = 7u,
|
||||
LED_IR_LED = 8u,
|
||||
LED_SPEAKER = 9u,
|
||||
LED_RFID = 10u,
|
||||
LED_KEYPAD = 11u,
|
||||
LED_LCD = 12u,
|
||||
LED_S4 = 13u,
|
||||
LED_S3 = 14u,
|
||||
LED_S2 = 15u,
|
||||
LED_S1 = 16u,
|
||||
LED_B4 = 17u,
|
||||
LED_B3 = 18u,
|
||||
LED_B2 = 19u,
|
||||
LED_B1 = 20u,
|
||||
LED_MAX = 20u,
|
||||
};
|
||||
|
||||
/// @brief Initializes the indicator LEDs
|
||||
void init_leds();
|
||||
|
||||
/// Sets the color of an LED.
|
||||
///
|
||||
/// Call `flush_leds()` to send the data to the LEDs.
|
||||
void led_set(uint32_t led, uint32_t color);
|
||||
|
||||
/// Sets the color of an LED with rgb.
|
||||
///
|
||||
/// Call `flush_leds()` to send the data to the LEDs.
|
||||
void led_set(uint32_t led, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/// Outputs the data to the leds.
|
||||
void leds_flush();
|
||||
|
||||
/// Clears the LEDs
|
||||
void leds_clear();
|
||||
|
||||
#endif /* LEDS_H */
|
||||
63
main/drivers/nvs.cpp
Normal file
63
main/drivers/nvs.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include "nvs.h"
|
||||
#include "esp_log.h"
|
||||
#include "bottom_half.h"
|
||||
#include "char_lcd.h"
|
||||
|
||||
static const char* TAG = "nvs";
|
||||
|
||||
static const char* HWDATA_NAMESPACE = "hwdata";
|
||||
static HWData hw_data;
|
||||
|
||||
bool init_nvs() {
|
||||
ESP_LOGI(TAG, "Initializing NVS...");
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
// NVS partition was truncated, erase and retry
|
||||
ESP_LOGE(TAG, "Failed to init nvs flash: %s.", esp_err_to_name(ret));
|
||||
lcd_print(1, 0, "NVS: ");
|
||||
lcd_print(1, 4, esp_err_to_name(ret));
|
||||
lcd_print(2, 0, "Press Yellow to skip");
|
||||
lcd_print(3, 0, "Press Red to erase");
|
||||
|
||||
ButtonKey button;
|
||||
while (!( get_button_pressed(&button) && (button == ButtonKey::button_red || button == ButtonKey::button_yellow) )) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
lcd_clear();
|
||||
|
||||
if (button == ButtonKey::button_yellow) {
|
||||
return false;
|
||||
}
|
||||
if (button == ButtonKey::button_red) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
nvs_handle_t hw_handle;
|
||||
ret = nvs_open(HWDATA_NAMESPACE, NVS_READONLY, &hw_handle);
|
||||
if (ret == ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_LOGW(TAG, "Partition \"%s\" has not been initialized. Loading defaults.", HWDATA_NAMESPACE);
|
||||
hw_data = HWData();
|
||||
} else {
|
||||
ESP_ERROR_CHECK(ret);
|
||||
hw_data = HWData::load(hw_handle);
|
||||
nvs_close(hw_handle);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "NVS initialized!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HWData& get_hw_data() {
|
||||
return hw_data;
|
||||
}
|
||||
|
||||
void save_hw_data(bool force) {
|
||||
nvs_handle_t hw_handle;
|
||||
ESP_ERROR_CHECK(nvs_open(HWDATA_NAMESPACE, NVS_READWRITE, &hw_handle));
|
||||
hw_data.save(hw_handle);
|
||||
nvs_close(hw_handle);
|
||||
}
|
||||
|
||||
14
main/drivers/nvs.h
Normal file
14
main/drivers/nvs.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef NVS_H
|
||||
#define NVS_H
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "hwdata.h"
|
||||
|
||||
/// @brief Initializes the NVS.
|
||||
bool init_nvs();
|
||||
|
||||
/// Gets the HWData stored in NVS.
|
||||
HWData& get_hw_data();
|
||||
|
||||
#endif /* NVS_H */
|
||||
2
main/drivers/perh.cpp
Normal file
2
main/drivers/perh.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "perh.h"
|
||||
|
||||
12
main/drivers/perh.h
Normal file
12
main/drivers/perh.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef PERH_H
|
||||
#define PERH_H
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#define PIN_PERH0 GPIO_NUM_6
|
||||
#define PIN_PERH1 GPIO_NUM_5
|
||||
#define PIN_PERH2 GPIO_NUM_4
|
||||
#define PIN_PERH3 GPIO_NUM_2
|
||||
#define PIN_PERH4 GPIO_NUM_1
|
||||
|
||||
#endif /* PERH_H */
|
||||
100
main/drivers/power.cpp
Normal file
100
main/drivers/power.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "power.h"
|
||||
#include "char_lcd.h"
|
||||
#include "starcode.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char* TAG = "power";
|
||||
|
||||
void bat_monitor_task(void* arg) {
|
||||
while (1) {
|
||||
char str_buf[33] = {0};
|
||||
uint16_t voltage = lipo.voltage();
|
||||
sprintf(str_buf, "%d.%03dV", voltage / 1000, voltage % 1000);
|
||||
|
||||
lcd_clear();
|
||||
lcd_print(0, 1, str_buf);
|
||||
|
||||
int16_t current = lipo.current();
|
||||
sprintf(str_buf, "%dmA", current);
|
||||
|
||||
lcd_print(1, 1, str_buf);
|
||||
|
||||
|
||||
int16_t total_cap = lipo.capacity(capacity_measure::FULL);
|
||||
sprintf(str_buf, "%dmAh", total_cap);
|
||||
|
||||
lcd_print(2, 1, str_buf);
|
||||
|
||||
int16_t soc = lipo.soc(soc_measure::FILTERED);
|
||||
sprintf(str_buf, "%d%%", soc);
|
||||
|
||||
lcd_print(3, 1, str_buf);
|
||||
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(250));
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void init_power_board() {
|
||||
ESP_LOGI(TAG, "Initializing power board...");
|
||||
|
||||
if (!lipo.begin()) {
|
||||
ESP_LOGE(TAG, "Failed to init communication with the battery gas guage");
|
||||
return;
|
||||
}
|
||||
|
||||
auto voltage = lipo.voltage();
|
||||
ESP_LOGI(TAG, "Battery Voltage: %d", voltage);
|
||||
|
||||
ESP_LOGI(TAG, "Power board initialized!");
|
||||
}
|
||||
|
||||
uint16_t get_bat_voltage() {
|
||||
return lipo.voltage();
|
||||
}
|
||||
|
||||
void lcd_print_header_bat() {
|
||||
if (!lcd_header_enabled()) return;
|
||||
if (lcd_starcode_displaying_result()) return;
|
||||
|
||||
uint8_t soc = lipo.soc();
|
||||
int16_t current = lipo.current();
|
||||
char buf[6];
|
||||
if (soc < 5 && current <= 0) {
|
||||
snprintf(buf, sizeof(buf), " LOW");
|
||||
} else if (soc == 100) {
|
||||
snprintf(buf, sizeof(buf), " 100");
|
||||
} else {
|
||||
if (current > 0) {
|
||||
snprintf(buf, sizeof(buf), " %2d+", soc);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), " %2d%%", soc);
|
||||
}
|
||||
}
|
||||
|
||||
lcd_print(0, 16, buf);
|
||||
}
|
||||
|
||||
// memory version
|
||||
// void lcd_print_header_bat() {
|
||||
// if (!lcd_header_enabled()) return;
|
||||
// if (lcd_starcode_displaying_result()) return;
|
||||
|
||||
// // Show memory usage percentage instead of battery percentage
|
||||
// char buf[6];
|
||||
// size_t free_heap = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
|
||||
// size_t total_heap = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
|
||||
// uint8_t mem_percent = 0;
|
||||
// if (total_heap > 0) {
|
||||
// mem_percent = (uint8_t)(100 - ((free_heap * 100) / total_heap));
|
||||
// }
|
||||
// if (mem_percent >= 100) {
|
||||
// snprintf(buf, sizeof(buf), " 100");
|
||||
// } else {
|
||||
// snprintf(buf, sizeof(buf), " %2d%%", mem_percent);
|
||||
// }
|
||||
|
||||
// lcd_print(0, 16, buf);
|
||||
// }
|
||||
20
main/drivers/power.h
Normal file
20
main/drivers/power.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef POWER_H
|
||||
#define POWER_H
|
||||
|
||||
#include "SparkFunBQ27441/SparkFunBQ27441.h"
|
||||
|
||||
extern volatile uint32_t battery_charge;
|
||||
|
||||
void bat_monitor_task(void* arg);
|
||||
|
||||
/// Initializes the battery gas guage for getting battery stats.
|
||||
void init_power_board();
|
||||
|
||||
/// @brief Gets the battery voltage.
|
||||
/// @return battery voltage in mV.
|
||||
uint16_t get_bat_voltage();
|
||||
|
||||
/// @brief Prints the battery section of the header to the char_lcd. (row 0, columns 17-19)
|
||||
void lcd_print_header_bat();
|
||||
|
||||
#endif /* POWER_H */
|
||||
86
main/drivers/sd.cpp
Normal file
86
main/drivers/sd.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "sd.h"
|
||||
#include "char_lcd.h"
|
||||
#include "bottom_half.h"
|
||||
|
||||
sdmmc_card_t *card;
|
||||
|
||||
static const char* mount_point = MOUNT_POINT;
|
||||
static const char* TAG = "sd";
|
||||
|
||||
bool init_sd() {
|
||||
ESP_LOGI(TAG, "Initializing SD card...");
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 8,
|
||||
.allocation_unit_size = 16 * 1024,
|
||||
.disk_status_check_enable = true,
|
||||
.use_one_fat = false,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Using SDMMC peripheral");
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
|
||||
sdmmc_slot_config_t slot_config = {
|
||||
.clk = SD_PIN_CLK,
|
||||
.cmd = SD_PIN_CMD,
|
||||
.d0 = SD_PIN_D0,
|
||||
.d1 = SD_PIN_D1,
|
||||
.d2 = SD_PIN_D2,
|
||||
.d3 = SD_PIN_D3,
|
||||
.d4 = GPIO_NUM_NC,
|
||||
.d5 = GPIO_NUM_NC,
|
||||
.d6 = GPIO_NUM_NC,
|
||||
.d7 = GPIO_NUM_NC,
|
||||
.cd = GPIO_NUM_NC,
|
||||
.wp = GPIO_NUM_NC,
|
||||
.width = 4,
|
||||
.flags = 0
|
||||
// .flags = SDMMC_SLOT_FLAG_INTERNAL_PULLUP
|
||||
};
|
||||
|
||||
try_mount:
|
||||
ESP_LOGI(TAG, "Mounting filesystem");
|
||||
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Filesystem mounted");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to mount sd card: %s.", esp_err_to_name(ret));
|
||||
|
||||
lcd_print(0, 0, "SD: ");
|
||||
lcd_print(0, 4, esp_err_to_name(ret));
|
||||
lcd_print(1, 0, "Press Green to retry");
|
||||
lcd_print(2, 0, "Press Yellow to skip");
|
||||
lcd_print(3, 0, "Press Red to format");
|
||||
|
||||
ButtonKey button;
|
||||
while (!( get_button_pressed(&button) && (button == ButtonKey::button_green || button == ButtonKey::button_red || button == ButtonKey::button_yellow) )) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
lcd_clear();
|
||||
|
||||
if (button == ButtonKey::button_green) {
|
||||
goto try_mount;
|
||||
}
|
||||
if (button == ButtonKey::button_yellow) {
|
||||
return false;
|
||||
}
|
||||
if (button == ButtonKey::button_red) {
|
||||
mount_config.format_if_mount_failed = true;
|
||||
goto try_mount;
|
||||
}
|
||||
}
|
||||
|
||||
// Card has been initialized, print its properties
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
|
||||
ESP_LOGI(TAG, "SD card initialized!");
|
||||
return true;
|
||||
}
|
||||
|
||||
void deinit_sd() {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_vfs_fat_sdcard_unmount(mount_point, card));
|
||||
ESP_LOGI(TAG, "Card unmounted");
|
||||
}
|
||||
31
main/drivers/sd.h
Normal file
31
main/drivers/sd.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef SD_H
|
||||
#define SD_H
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
|
||||
#define MOUNT_POINT "/sd"
|
||||
|
||||
extern sdmmc_card_t *card;
|
||||
|
||||
#define SD_PIN_CLK GPIO_NUM_39
|
||||
#define SD_PIN_CMD GPIO_NUM_40
|
||||
#define SD_PIN_D0 GPIO_NUM_38
|
||||
#define SD_PIN_D1 GPIO_NUM_45
|
||||
#define SD_PIN_D2 GPIO_NUM_42
|
||||
#define SD_PIN_D3 GPIO_NUM_41
|
||||
|
||||
/// @brief Initializes the SD card
|
||||
///
|
||||
/// This requires the char_lcd to have been initialized.
|
||||
/// @return true if the sd card was successfully initialized
|
||||
bool init_sd();
|
||||
|
||||
/// @brief Unmounts the sd card
|
||||
void deinit_sd();
|
||||
|
||||
#endif /* SD_H */
|
||||
261
main/drivers/speaker.cpp
Normal file
261
main/drivers/speaker.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
#include "speaker.h"
|
||||
#include "state_tracking.h"
|
||||
|
||||
static const char *TAG = "speaker";
|
||||
|
||||
static size_t audio_block_size = 4096;
|
||||
|
||||
typedef struct {
|
||||
/// The path to the file being played.
|
||||
char* file_name;
|
||||
/// The handle to the file being played.
|
||||
FILE* file_handle;
|
||||
/// A number that all samples will be shifted right by.
|
||||
uint8_t prescaler;
|
||||
/// A flag for repeating.
|
||||
bool repeat;
|
||||
} playing_audio_clip_t;
|
||||
|
||||
// It's hard to get the tx_chan's internal state, so instead, we keep track of it here.
|
||||
bool channel_enabled = false;
|
||||
i2s_chan_handle_t tx_chan;
|
||||
|
||||
/// A queue of owned `char*` to be stopped from playing.
|
||||
///
|
||||
/// the speaker driver system will free the given char* when it is done with it.
|
||||
QueueHandle_t stop_clip_queue;
|
||||
/// A queue of `audio_clip_t`s to be played once there is nothing being played
|
||||
QueueHandle_t play_clip_queue;
|
||||
|
||||
/// The clips that are currently playing
|
||||
std::vector<playing_audio_clip_t> playing_clips;
|
||||
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
if (strcmp(event, "PLAY_WAV") == 0) {
|
||||
char* file_name = strtok(arg, ":");
|
||||
char* play_immediately_str = strtok(NULL, ":");
|
||||
char* repeat_str = strtok(NULL, ":");
|
||||
char* prescaler_str = strtok(NULL, ":");
|
||||
|
||||
bool play_immediately = strcmp(play_immediately_str, "true") == 0;
|
||||
bool repeat = strcmp(repeat_str, "true") == 0;
|
||||
uint8_t prescaler = atoi(prescaler_str);
|
||||
play_clip_wav(file_name, play_immediately, repeat, prescaler, 0);
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "STOP_WAV") == 0) {
|
||||
stop_clip(arg, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool push_clip(audio_clip_t clip) {
|
||||
ESP_LOGD(TAG, "playing %s", clip.file_name);
|
||||
FILE* fh = fopen(clip.file_name, "rb");
|
||||
if (fh == NULL) {
|
||||
ESP_LOGW(TAG, "failed to open %s", clip.file_name);
|
||||
return false;
|
||||
} else {
|
||||
// skip the .wav header
|
||||
fseek(fh, 44, SEEK_SET);
|
||||
|
||||
playing_audio_clip_t playing_clip = {
|
||||
.file_name = clip.file_name,
|
||||
.file_handle = fh,
|
||||
.prescaler = clip.prescaler,
|
||||
.repeat = clip.repeat,
|
||||
};
|
||||
playing_clips.push_back(playing_clip);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Disables the channel if neccesary.
|
||||
static void disable_channel() {
|
||||
if (channel_enabled) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_disable(tx_chan));
|
||||
channel_enabled = false;
|
||||
}
|
||||
}
|
||||
/// @brief Enables the channel if neccesary
|
||||
static void enable_channel() {
|
||||
if (!channel_enabled) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_enable(tx_chan));
|
||||
channel_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void speaker_task(void* arg) {
|
||||
audio_clip_t next_clip;
|
||||
char* clip_to_stop;
|
||||
|
||||
int16_t* audio_buf = (int16_t*) malloc(audio_block_size * sizeof(int16_t));
|
||||
int16_t* file_buf = (int16_t*) malloc(audio_block_size * sizeof(int16_t));
|
||||
|
||||
while (1) {
|
||||
// first, take all "play immediatly" clips from the queue.
|
||||
while (xQueuePeek(play_clip_queue, &next_clip, 0) == pdTRUE && next_clip.play_immediatly) {
|
||||
if (xQueueReceive(play_clip_queue, &next_clip, 0) == pdTRUE) {
|
||||
push_clip(next_clip);
|
||||
}
|
||||
}
|
||||
|
||||
// handle any stop requests
|
||||
while (xQueueReceive(stop_clip_queue, &clip_to_stop, 0) == pdTRUE) {
|
||||
// delete clip from list
|
||||
bool found = false;
|
||||
for (int clip_idx = playing_clips.size()-1; clip_idx >= 0; clip_idx--) {
|
||||
playing_audio_clip_t& clip = playing_clips.at(clip_idx);
|
||||
if (strcmp(clip.file_name, clip_to_stop) == 0) {
|
||||
found = true;
|
||||
ESP_LOGV(TAG, "stopping %s", clip.file_name);
|
||||
fclose(clip.file_handle);
|
||||
free(clip.file_name);
|
||||
playing_clips.erase(playing_clips.begin() + clip_idx);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ESP_LOGW(TAG, "Could not find clip \"%s\" to stop", clip_to_stop);
|
||||
}
|
||||
// free the string that was passed into the queue
|
||||
free(clip_to_stop);
|
||||
}
|
||||
|
||||
// if we aren't playing any clips, wait for the next one.
|
||||
if (playing_clips.empty()) {
|
||||
if (xQueueReceive(play_clip_queue, &next_clip, 0) == pdTRUE) {
|
||||
push_clip(next_clip);
|
||||
} else {
|
||||
// we must wait before our next clip
|
||||
disable_channel();
|
||||
// now wait for next clip
|
||||
if (xQueueReceive(play_clip_queue, &next_clip, portMAX_DELAY) == pdTRUE) {
|
||||
push_clip(next_clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send 1 block
|
||||
std::memset(audio_buf, 0, audio_block_size * sizeof(int16_t));
|
||||
|
||||
for (int clip_idx = playing_clips.size()-1; clip_idx >= 0; clip_idx--) {
|
||||
playing_audio_clip_t& clip = playing_clips.at(clip_idx);
|
||||
|
||||
size_t samples_read = fread(file_buf, sizeof(uint16_t), audio_block_size, clip.file_handle);
|
||||
if (samples_read == 0) {
|
||||
if (clip.repeat) {
|
||||
ESP_LOGV(TAG, "repeating %s", clip.file_name);
|
||||
fseek(clip.file_handle, 44, SEEK_SET);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "finishing %s", clip.file_name);
|
||||
fclose(clip.file_handle);
|
||||
free(clip.file_name);
|
||||
playing_clips.erase(playing_clips.begin() + clip_idx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samples_read; i++) {
|
||||
audio_buf[i] += (file_buf[i] >> clip.prescaler);
|
||||
}
|
||||
}
|
||||
|
||||
size_t bytes_to_write = audio_block_size * sizeof(int16_t);
|
||||
size_t bytes_written;
|
||||
if (!channel_enabled) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_preload_data(tx_chan, audio_buf, bytes_to_write, &bytes_written));
|
||||
enable_channel();
|
||||
} else {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_write(tx_chan, audio_buf, bytes_to_write, &bytes_written, portMAX_DELAY));
|
||||
}
|
||||
if (bytes_written != bytes_to_write) {
|
||||
ESP_LOGW(TAG, "only %d bytes written to the i2s channel! (expected %d)", bytes_written, bytes_to_write);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
bool play_clip_wav(const char* file_name, bool play_immediately, bool repeat, uint8_t prescaler, TickType_t ticks_to_wait) {
|
||||
// clone the passed in string to the heap.
|
||||
size_t len = strlen(file_name);
|
||||
char* dynamic_file_name = (char*) malloc(len+1);
|
||||
strcpy(dynamic_file_name, file_name);
|
||||
|
||||
audio_clip_t clip = {
|
||||
.file_name = dynamic_file_name,
|
||||
.prescaler = prescaler,
|
||||
.repeat = repeat,
|
||||
.play_immediatly = play_immediately,
|
||||
};
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%s:%d:%d:%d", file_name, play_immediately, repeat, prescaler);
|
||||
event_occured("PLAY_WAV", buf);
|
||||
}
|
||||
|
||||
if (play_immediately) {
|
||||
return xQueueSendToFront(play_clip_queue, &clip, ticks_to_wait) == pdTRUE;
|
||||
}
|
||||
return xQueueSend(play_clip_queue, &clip, ticks_to_wait) == pdTRUE;
|
||||
}
|
||||
|
||||
bool stop_clip(const char* file_name, TickType_t ticks_to_wait) {
|
||||
// clone the passed in string to the heap.
|
||||
size_t len = strlen(file_name);
|
||||
char* dynamic_file_name = (char*) malloc(len+1);
|
||||
strcpy(dynamic_file_name, file_name);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("STOP_WAV", file_name);
|
||||
}
|
||||
|
||||
return xQueueSend(stop_clip_queue, &dynamic_file_name, ticks_to_wait) == pdTRUE;
|
||||
}
|
||||
|
||||
void init_speaker(void) {
|
||||
ESP_LOGI(TAG, "Initializing speaker...");
|
||||
|
||||
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||
|
||||
i2s_std_config_t tx_std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = SPEAKER_PIN_BCLK,
|
||||
.ws = SPEAKER_PIN_WS,
|
||||
.dout = SPEAKER_PIN_DOUT,
|
||||
.din = GPIO_NUM_NC,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
|
||||
|
||||
i2s_chan_info_t info = {};
|
||||
i2s_channel_get_info(tx_chan, &info);
|
||||
if (info.total_dma_buf_size != 0) {
|
||||
audio_block_size = info.total_dma_buf_size / sizeof(int16_t);
|
||||
}
|
||||
|
||||
// init queues
|
||||
play_clip_queue = xQueueCreate(CLIP_QUEUE_SIZE, sizeof(audio_clip_t));
|
||||
stop_clip_queue = xQueueCreate(CLIP_QUEUE_SIZE, sizeof(char*));
|
||||
|
||||
// start task
|
||||
xTaskCreate(speaker_task, "play_audio", 4096, NULL, 5, NULL);
|
||||
|
||||
play_clip_wav(MOUNT_POINT "/startup.wav", true, false, 4, 0);
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
ESP_LOGI(TAG, "Speaker initialized!");
|
||||
}
|
||||
63
main/drivers/speaker.h
Normal file
63
main/drivers/speaker.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef SPEAKER_H
|
||||
#define SPEAKER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "sd.h"
|
||||
|
||||
#define SPEAKER_PIN_BCLK GPIO_NUM_11
|
||||
#define SPEAKER_PIN_WS GPIO_NUM_12
|
||||
#define SPEAKER_PIN_DOUT GPIO_NUM_3
|
||||
#define SAMPLE_RATE 44100
|
||||
// The maximum number of clips that can be queued at one time.
|
||||
#define CLIP_QUEUE_SIZE 64
|
||||
|
||||
extern i2s_chan_handle_t tx_chan;
|
||||
|
||||
typedef struct {
|
||||
/// A dynamically allocated string specifying the name of the
|
||||
/// file to play. The speaker system will free this once it
|
||||
/// is done.
|
||||
char* file_name;
|
||||
/// A number that all samples will be shifted right by.
|
||||
uint8_t prescaler;
|
||||
/// A flag for repeating.
|
||||
bool repeat;
|
||||
/// A flag for starting the clip immediatly,
|
||||
/// even if there is another clip playing.
|
||||
bool play_immediatly;
|
||||
} audio_clip_t;
|
||||
|
||||
/// @brief Initalizes the speaker
|
||||
void init_speaker();
|
||||
|
||||
/// @brief Plays a wav audio clip.
|
||||
/// @param file_name The name of the file to play. Fully qualified with the `.wav` extention.
|
||||
/// @param play_immediatly If `true`, the file will play immediatly even if there is already
|
||||
/// another clip being played. The two clips will play simultaniously. If `false` the clip
|
||||
/// will play once all the previous clips have finished.
|
||||
/// @param repeat If `true` the clip will repeat until stopped. If `false` the clip will
|
||||
/// stop when finished.
|
||||
/// @param prescaler A number to shift right all samples by. This makes the music half as
|
||||
/// loud. Useful for loud audio files.
|
||||
/// @param ticks_to_wait The number of ticks to wait if the queue is full.
|
||||
/// @return true if the clip was added to the queue.
|
||||
bool play_clip_wav(const char* file_name, bool play_immediately, bool repeat, uint8_t prescaler, TickType_t ticks_to_wait);
|
||||
|
||||
/// @brief Stops an audio clip from playing.
|
||||
/// @param file_name The file name of the audio clip that was played.
|
||||
/// @param ticks_to_wait The number of ticks to wait if the queue is full.
|
||||
/// @return true if the clip was added to the queue.
|
||||
bool stop_clip(const char* file_name, TickType_t ticks_to_wait);
|
||||
|
||||
#endif /* SPEAKER_H */
|
||||
138
main/drivers/sseg.cpp
Normal file
138
main/drivers/sseg.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "sseg.h"
|
||||
#include <esp_log.h>
|
||||
#include "state_tracking.h"
|
||||
#include <cstring>
|
||||
|
||||
TM1640* sseg = nullptr;
|
||||
|
||||
static const char *TAG = "sseg";
|
||||
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
if (strcmp(event, "SSEG_G_RAW") == 0) {
|
||||
uint8_t segments[4];
|
||||
segments[0] = atoi(strtok(arg, ","));
|
||||
for (int i = 1; i < 4; i++) {
|
||||
segments[i] = atoi(strtok(NULL, ","));
|
||||
}
|
||||
set_game_sseg_raw(segments);
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "SSEG_G_CLR") == 0) {
|
||||
clear_game_sseg();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "SSEG_M_RAW") == 0) {
|
||||
uint8_t segments[4];
|
||||
segments[0] = atoi(strtok(arg, ","));
|
||||
for (int i = 1; i < 4; i++) {
|
||||
segments[i] = atoi(strtok(NULL, ","));
|
||||
}
|
||||
set_module_sseg_raw(segments);
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "SSEG_M_CLR") == 0) {
|
||||
clear_module_sseg();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "SSEG_G") == 0) {
|
||||
uint32_t value = atoi(strtok(arg, ","));
|
||||
uint8_t dot_pos = atoi(strtok(NULL, ","));
|
||||
set_game_sseg_num(value, dot_pos);
|
||||
return true;
|
||||
}
|
||||
if (strcmp(event, "SSEG_M") == 0) {
|
||||
uint32_t value = atoi(strtok(arg, ","));
|
||||
uint8_t dot_pos = atoi(strtok(NULL, ","));
|
||||
set_module_sseg_num(value, dot_pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_sseg() {
|
||||
ESP_LOGI(TAG, "Initializing sseg...");
|
||||
|
||||
sseg = new TM1640(SSEG_PIN_DATA, SSEG_PIN_CLK, 8);
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
ESP_LOGI(TAG, "Sseg initialized!");
|
||||
}
|
||||
|
||||
void set_game_sseg_raw(const uint8_t* segments) {
|
||||
sseg->setSegments(segments[0], 0);
|
||||
sseg->setSegments(segments[1], 1);
|
||||
sseg->setSegments(segments[2], 2);
|
||||
sseg->setSegments(segments[3], 3);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[32];
|
||||
sprintf(buf, "%d,%d,%d,%d", segments[0], segments[1], segments[2], segments[3]);
|
||||
event_occured("SSEG_G_RAW", buf);
|
||||
}
|
||||
}
|
||||
void clear_game_sseg() {
|
||||
sseg->setSegments(0, 0);
|
||||
sseg->setSegments(0, 1);
|
||||
sseg->setSegments(0, 2);
|
||||
sseg->setSegments(0, 3);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("SSEG_G_CLR", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void set_module_sseg_raw(const uint8_t* segments) {
|
||||
sseg->setSegments(segments[0], 4);
|
||||
sseg->setSegments(segments[1], 5);
|
||||
sseg->setSegments(segments[2], 6);
|
||||
sseg->setSegments(segments[3], 7);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[32];
|
||||
sprintf(buf, "%d,%d,%d,%d", segments[0], segments[1], segments[2], segments[3]);
|
||||
event_occured("SSEG_M_RAW", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void clear_module_sseg() {
|
||||
sseg->setSegments(0, 4);
|
||||
sseg->setSegments(0, 5);
|
||||
sseg->setSegments(0, 6);
|
||||
sseg->setSegments(0, 7);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
event_occured("SSEG_M_CLR", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void set_game_sseg_num(uint32_t value, uint8_t dot_pos) {
|
||||
uint32_t initial_value = value;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto idx = value % 10;
|
||||
sseg->sendChar(3-i, TM16XX_NUMBER_FONT[idx], i == dot_pos);
|
||||
value = value / 10;
|
||||
}
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[16];
|
||||
sprintf(buf, "%ld,%d", initial_value, dot_pos);
|
||||
event_occured("SSEG_G", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void set_module_sseg_num(uint32_t value, uint8_t dot_pos) {
|
||||
uint32_t initial_value = value;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto idx = value % 10;
|
||||
sseg->sendChar(7-i, TM16XX_NUMBER_FONT[idx], i == dot_pos);
|
||||
value = value / 10;
|
||||
}
|
||||
|
||||
if (is_state_tracking()) {
|
||||
char buf[16];
|
||||
sprintf(buf, "%ld,%d", initial_value, dot_pos);
|
||||
event_occured("SSEG_M", buf);
|
||||
}
|
||||
}
|
||||
41
main/drivers/sseg.h
Normal file
41
main/drivers/sseg.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef SSEG_H
|
||||
#define SSEG_H
|
||||
|
||||
#include "TM1640/TM1640.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define SSEG_PIN_DATA GPIO_NUM_46
|
||||
#define SSEG_PIN_CLK GPIO_NUM_48
|
||||
|
||||
extern TM1640* sseg;
|
||||
|
||||
/// Initializes the seven segment driver
|
||||
void init_sseg();
|
||||
|
||||
/// @brief Sets the game seven segment to the value of `segments`.
|
||||
///
|
||||
/// the bits 0-6 map to segments A-G, and bit 7 is DP.
|
||||
///
|
||||
/// `digits` must have len >= 4.
|
||||
void set_game_sseg_raw(const uint8_t* segments);
|
||||
|
||||
/// @brief Clears the game seven segment display.
|
||||
void clear_game_sseg();
|
||||
|
||||
/// @brief Sets the module seven segment to the value of the `segments`.
|
||||
///
|
||||
/// the bits 0-6 map to segments A-G, and bit 7 is DP.
|
||||
///
|
||||
/// `digits` must have len >= 4.
|
||||
void set_module_sseg_raw(const uint8_t* segments);
|
||||
|
||||
/// @brief Clears the game seven segment display.
|
||||
void clear_module_sseg();
|
||||
|
||||
/// sets the game timer to the given number, with the dot at position `dot_pos` from the right (0 => right-most digit).
|
||||
void set_game_sseg_num(uint32_t value, uint8_t dot_pos);
|
||||
|
||||
/// sets the module timer to the given number, with the dot at position `dot_pos` from the right (0 => right-most digit).
|
||||
void set_module_sseg_num(uint32_t value, uint8_t dot_pos);
|
||||
|
||||
#endif /* SSEG_H */
|
||||
275
main/drivers/starcode.cpp
Normal file
275
main/drivers/starcode.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
#include "starcode.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <esp_log.h>
|
||||
#include "drivers/bottom_half.h"
|
||||
#include "char_lcd.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char* TAG = "star_code";
|
||||
|
||||
volatile bool handling_new_starcodes = false;
|
||||
static volatile bool system_initialized = false;
|
||||
|
||||
// TODO: use the semaphore, convert to RWLock?
|
||||
static volatile SemaphoreHandle_t star_codes_mutex;
|
||||
static std::vector<StarCodeEntry> star_codes;
|
||||
|
||||
static const char EMPTY_STAR_CODE_HEADER[] = " ";
|
||||
|
||||
esp_timer_handle_t starcode_delay_timer;
|
||||
/// @brief `true` if we are delaying for a starcode
|
||||
static volatile bool delaying_for_starcode;
|
||||
static volatile StarCodeEntry* current_starcode = nullptr;
|
||||
/// @brief `true` when we are handling user input for a starcode
|
||||
static volatile bool doing_starcode = false;
|
||||
static uint16_t starcode_waiting_on_release;
|
||||
static char current[STARCODE_MAX_LEN + 1];
|
||||
static size_t current_idx;
|
||||
|
||||
// Task handle for the starcode callback task
|
||||
static TaskHandle_t starcode_callback_task_handle = nullptr;
|
||||
|
||||
static void starcode_callback_task(void* arg) {
|
||||
(void) arg;
|
||||
|
||||
while (true) {
|
||||
// Wait for notification from starcode_trigger_cb
|
||||
uint32_t notification_value;
|
||||
if (xTaskNotifyWait(0, ULONG_MAX, ¬ification_value, portMAX_DELAY) == pdTRUE) {
|
||||
// Process the starcode callback
|
||||
delaying_for_starcode = false;
|
||||
lcd_print_header();
|
||||
|
||||
if (current_starcode != nullptr) {
|
||||
if (current_starcode->triggered_sem != nullptr)
|
||||
xSemaphoreGive(current_starcode->triggered_sem);
|
||||
if (current_starcode->callback != nullptr)
|
||||
(current_starcode->callback)();
|
||||
|
||||
current_starcode = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void starcode_trigger_cb(void* arg) {
|
||||
(void) arg;
|
||||
|
||||
if (starcode_callback_task_handle != nullptr) {
|
||||
xTaskNotify(starcode_callback_task_handle, 1, eSetBits);
|
||||
}
|
||||
}
|
||||
// TODO: rename star code everywhere to starcode
|
||||
|
||||
|
||||
void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) {
|
||||
if ((!delaying_for_starcode) && handling_new_starcodes && (*just_pressed & (1 << KeypadKey::star))) {
|
||||
current_idx = 0;
|
||||
current[current_idx] = '\0';
|
||||
doing_starcode = true;
|
||||
}
|
||||
if (doing_starcode) {
|
||||
// If we get a press while handling a starcode, we also want to capture the release of that key.
|
||||
starcode_waiting_on_release |= *just_pressed;
|
||||
|
||||
KeypadKey key;
|
||||
while (take_key(&key, just_pressed)) {
|
||||
if (key == KeypadKey::star) {
|
||||
current_idx = 0;
|
||||
current[current_idx] = '\0';
|
||||
} else if (key == KeypadKey::pound) {
|
||||
doing_starcode = false;
|
||||
if (current_idx != 0) {
|
||||
trigger_star_code(current);
|
||||
}
|
||||
} else {
|
||||
// shift the digits left if neccesary
|
||||
if (current_idx >= STARCODE_MAX_LEN) {
|
||||
for (int i = 1; i < current_idx; i++) {
|
||||
current[i-1] = current[i];
|
||||
}
|
||||
current_idx--;
|
||||
}
|
||||
// append the character
|
||||
current[current_idx++] = char_of_keypad_key(key);
|
||||
current[current_idx] = '\0';
|
||||
}
|
||||
lcd_print_header_star_code();
|
||||
}
|
||||
}
|
||||
|
||||
// capture any releases from starcodes
|
||||
uint16_t new_just_released = (*just_released) & (~starcode_waiting_on_release);
|
||||
starcode_waiting_on_release = starcode_waiting_on_release & (~*just_released);
|
||||
*just_released = new_just_released;
|
||||
}
|
||||
|
||||
void init_star_code_system() {
|
||||
star_codes_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
xTaskCreate(starcode_callback_task, "starcode_cb", 4096, NULL, 3, &starcode_callback_task_handle);
|
||||
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
.callback = &starcode_trigger_cb,
|
||||
.arg = nullptr,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "starcode_trigger",
|
||||
.skip_unhandled_events = false,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &starcode_delay_timer));
|
||||
|
||||
handling_new_starcodes = true;
|
||||
system_initialized = true;
|
||||
}
|
||||
|
||||
/// Checks if a triggered code matches an expected code.
|
||||
/// @return true iff the codes match, where '*'s in the expected code can match any character in the triggered code
|
||||
static bool check_code_match(const char* triggered, const char* expected) {
|
||||
size_t triggered_len = strlen(triggered);
|
||||
size_t match_len = strlen(triggered);
|
||||
|
||||
if (triggered_len != match_len)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < triggered_len; i++) {
|
||||
if (!(expected[i] == '*' || expected[i] == triggered[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_star_code(StarCodeEntry code) {
|
||||
ESP_LOGI(TAG, "Adding starcode: %s", code.code);
|
||||
if (code.code == nullptr || strlen(code.code) > STARCODE_MAX_LEN) {
|
||||
ESP_LOGW(TAG, "invalid code");
|
||||
return false;
|
||||
}
|
||||
if (code.display_text != nullptr && strlen(code.display_text) > STARCODE_DISPLAY_TEXT_MAX_LEN) {
|
||||
ESP_LOGW(TAG, "invalid display_text");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for a existing entry
|
||||
auto it = std::find_if(star_codes.begin(), star_codes.end(), [&](const StarCodeEntry& other) {
|
||||
return check_code_match(code.code, other.code);
|
||||
});
|
||||
|
||||
if (it != star_codes.end()) {
|
||||
// existing star code found!
|
||||
ESP_LOGW(TAG, "Duplicate starcode %s", code.code);
|
||||
return false;
|
||||
}
|
||||
|
||||
star_codes.push_back(code);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_star_codes(const StarCodeEntry* codes, size_t len) {
|
||||
bool success = true;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!add_star_code(codes[i])) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool rm_star_code(const char* code) {
|
||||
ESP_LOGI(TAG, "Removing starcode: %s", code);
|
||||
|
||||
auto it = std::find_if(star_codes.begin(), star_codes.end(), [&](const StarCodeEntry& star_code) {
|
||||
return strcmp(code, star_code.code) == 0;
|
||||
});
|
||||
|
||||
if (it == star_codes.end()) {
|
||||
ESP_LOGW(TAG, "Failed to remove star code %s", code);
|
||||
return false;
|
||||
}
|
||||
|
||||
star_codes.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rm_star_codes(const StarCodeEntry* codes, size_t len) {
|
||||
bool success = true;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!rm_star_code(codes[i].code)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool rm_star_codes_str(const char** codes, size_t len) {
|
||||
bool success = true;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!rm_star_code(codes[i])) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void clear_star_codes() {
|
||||
star_codes.clear();
|
||||
}
|
||||
|
||||
bool trigger_star_code(const char* code) {
|
||||
auto it = std::find_if(star_codes.begin(), star_codes.end(), [&](const StarCodeEntry& other) {
|
||||
return check_code_match(code, other.code);
|
||||
});
|
||||
|
||||
uint64_t delay_us = 2'000'000;
|
||||
delaying_for_starcode = true;
|
||||
if (it != star_codes.end()) {
|
||||
current_starcode = &*it;
|
||||
delay_us = current_starcode->delay_us;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(starcode_delay_timer, delay_us));
|
||||
|
||||
return current_starcode != nullptr;
|
||||
}
|
||||
|
||||
void pause_star_code_system() {
|
||||
doing_starcode = false;
|
||||
handling_new_starcodes = false;
|
||||
}
|
||||
|
||||
void resume_star_code_system() {
|
||||
handling_new_starcodes = system_initialized;
|
||||
}
|
||||
|
||||
void lcd_print_header_star_code() {
|
||||
if (!lcd_header_enabled()) return;
|
||||
|
||||
// TODO: consider upping the display text size to be able to overwrite the game_state area.
|
||||
if (delaying_for_starcode) {
|
||||
if (current_starcode == nullptr) {
|
||||
lcd_print(0, 0, "Invalid starcode ");
|
||||
} else if (current_starcode->display_text != nullptr) {
|
||||
char buf[21];
|
||||
snprintf(buf, sizeof(buf), "%-20s", current_starcode->display_text);
|
||||
lcd_print(0, 0, buf);
|
||||
} else {
|
||||
lcd_print(0, 0, EMPTY_STAR_CODE_HEADER);
|
||||
}
|
||||
} else if (doing_starcode) {
|
||||
char buf[STARCODE_MAX_LEN + 2];
|
||||
snprintf(buf, sizeof(buf), "*%-9s", current);
|
||||
lcd_print(0, 0, buf);
|
||||
} else {
|
||||
lcd_print(0, 0, EMPTY_STAR_CODE_HEADER);
|
||||
}
|
||||
}
|
||||
|
||||
bool lcd_starcode_displaying_result() {
|
||||
return delaying_for_starcode;
|
||||
}
|
||||
90
main/drivers/starcode.h
Normal file
90
main/drivers/starcode.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef STAR_CODE_H
|
||||
#define STAR_CODE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
/// The max length of a starcode (not counting the star)
|
||||
#define STARCODE_MAX_LEN 9
|
||||
#define STARCODE_DISPLAY_TEXT_MAX_LEN 20
|
||||
|
||||
/// @brief A handler for a specific star code
|
||||
struct StarCodeEntry {
|
||||
/// @brief The star code without the star
|
||||
///
|
||||
/// This must be <= 9 characters.
|
||||
///
|
||||
/// You may include a * in the code to match on any character
|
||||
const char* code;
|
||||
/// @brief The text to display when the star code is entered (or null).
|
||||
///
|
||||
/// This must be <= 20 characters.
|
||||
const char* display_text;
|
||||
/// @brief The number of microseconds to delay when the star code is entered before calling the handler.
|
||||
uint64_t delay_us;
|
||||
/// @brief The function to call when the star code is entered.
|
||||
/// Can be null.
|
||||
void (*callback)(void);
|
||||
/// @brief The binary semaphore that will be given when this star code is triggered.
|
||||
/// Can be null.
|
||||
SemaphoreHandle_t triggered_sem;
|
||||
};
|
||||
|
||||
/// @brief Initializes the star code system.
|
||||
void init_star_code_system();
|
||||
|
||||
/// @brief Handles any keypad presses and releases before they bubble up to the rest of the BLK_BOX.
|
||||
void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released);
|
||||
|
||||
/// @brief Adds a star code to be handled.
|
||||
/// @param code the star code to add
|
||||
/// @return true iff the star code was added
|
||||
bool add_star_code(StarCodeEntry code);
|
||||
|
||||
/// @brief Adds a list of star codes to be handled.
|
||||
/// @param codes the list of star codes to add
|
||||
/// @param len the length of the list
|
||||
/// @return true iff all the star codes were added
|
||||
bool add_star_codes(const StarCodeEntry* codes, size_t len);
|
||||
|
||||
/// @brief removes a star code to stop handling it.
|
||||
/// @param code the star code to remove
|
||||
/// @return true iff the star code was removed
|
||||
bool rm_star_code(const char* code);
|
||||
|
||||
/// @brief removes all given star codes to stop handling them.
|
||||
/// @param codes the list of star codes to remove
|
||||
/// @param len the length of the list
|
||||
/// @return true iff all star codes were removed
|
||||
bool rm_star_codes(const StarCodeEntry* codes, size_t len);
|
||||
|
||||
/// @brief removes all given star codes to stop handling them.
|
||||
/// @param codes the list of star codes to remove
|
||||
/// @param len the length of the list
|
||||
/// @return true iff all star codes were removed
|
||||
bool rm_star_codes_str(const char** codes, size_t len);
|
||||
|
||||
/// @brief clears all star codes.
|
||||
void clear_star_codes();
|
||||
|
||||
/// @brief Triggers the given star code.
|
||||
/// @param code the star code to trigger (without the *)
|
||||
/// @return true iff a star code was triggered
|
||||
bool trigger_star_code(const char* code);
|
||||
|
||||
|
||||
/// @brief Starts/stops the star code system from handling new star codes.
|
||||
/// If one is being handled currently, it is canceled.
|
||||
void set_star_code_sys_enabled(bool enable);
|
||||
|
||||
/// @return `true` iff the star code system is handling star codes.
|
||||
bool star_code_sys_enabled();
|
||||
|
||||
/// @brief Prints the star code section of the header to the char_lcd. (row 0, columns 0-9)
|
||||
void lcd_print_header_star_code();
|
||||
|
||||
/// @return `true` iff the starcode system is using the full header.
|
||||
bool lcd_starcode_displaying_result();
|
||||
|
||||
#endif /* STAR_CODE_H */
|
||||
225
main/drivers/state_tracking.cpp
Normal file
225
main/drivers/state_tracking.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
#include "state_tracking.h"
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include "wlvgl.h"
|
||||
|
||||
static const char* PLAYBACK_TAG = "playback";
|
||||
|
||||
enum state_t {
|
||||
STATE_IDLE = 0,
|
||||
STATE_RECORDING = 1,
|
||||
STATE_PLAYBACK = 2,
|
||||
};
|
||||
|
||||
static std::vector<bool(*)(const char*, char*)> replay_fns;
|
||||
|
||||
static bool should_close_recording_stream;
|
||||
static FILE* recording_stream;
|
||||
static uint32_t recording_start_time;
|
||||
TaskHandle_t flush_file_task_handle;
|
||||
|
||||
static bool should_close_playback_stream;
|
||||
static FILE* playback_stream;
|
||||
static uint32_t playback_start_time;
|
||||
TaskHandle_t playback_task_handle;
|
||||
|
||||
static volatile state_t state = STATE_IDLE;
|
||||
|
||||
/// @brief Periodically flushes and syncs (if neccesary) the output stream.
|
||||
/// @param arg unused.
|
||||
static void flush_file_task(void* arg) {
|
||||
while (state == STATE_RECORDING && recording_stream != nullptr) {
|
||||
fflush(recording_stream);
|
||||
|
||||
int fd = fileno(recording_stream);
|
||||
if (fd != -1) {
|
||||
fsync(fd);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
}
|
||||
|
||||
flush_file_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void playback_task(void* arg) {
|
||||
const size_t size = 16*1024;
|
||||
char* buf = (char*) malloc(size*sizeof(char));
|
||||
if (buf == nullptr) {
|
||||
ESP_LOGE(PLAYBACK_TAG, "buf alloc failure");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
while (state == STATE_PLAYBACK && playback_stream != nullptr) {
|
||||
char* ret = fgets(buf, size, playback_stream);
|
||||
if (ret == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (buf[0] == '\0') {
|
||||
ESP_LOGI(PLAYBACK_TAG, "playback done");
|
||||
break;
|
||||
}
|
||||
// get rid of the '\n' and possibly '\r' in the string
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (buf[i] == '\0') break;
|
||||
if (buf[i] == '\r' || buf[i] == '\n') {
|
||||
buf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(PLAYBACK_TAG, "handling: %s", buf);
|
||||
|
||||
// look for a comma, indicating the end of the "ticks" part
|
||||
// TODO: replace with strtok
|
||||
size_t comma_pos = 0;
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (buf[i] == ',') {
|
||||
comma_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comma_pos == 0) {
|
||||
ESP_LOGE(PLAYBACK_TAG, "Failed to find comma in playback line");
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[comma_pos] = '\0';
|
||||
uint32_t tick = atoi(buf);
|
||||
|
||||
// now look for the colon to indicate the end of the event name
|
||||
size_t colon_pos = 0;
|
||||
int i = comma_pos + 1; // start looking right after the comma
|
||||
while (i < size) {
|
||||
if (buf[i] == ':') {
|
||||
colon_pos = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (colon_pos == 0) {
|
||||
ESP_LOGE(PLAYBACK_TAG, "Failed to find colon in playback line");
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[colon_pos] = '\0';
|
||||
char* event_name = buf + (comma_pos + 1);
|
||||
char* arg = buf + (colon_pos + 1);
|
||||
|
||||
int32_t ticks_to_wait = ((int32_t) tick) - (int32_t)(xTaskGetTickCount() - playback_start_time);
|
||||
|
||||
if (ticks_to_wait < 0) {
|
||||
ESP_LOGW(PLAYBACK_TAG, "Playback is behind by %ld ticks!", ticks_to_wait);
|
||||
}
|
||||
if (ticks_to_wait > 0) {
|
||||
vTaskDelay(ticks_to_wait);
|
||||
}
|
||||
|
||||
bool matched = false;
|
||||
for (const auto& fn : replay_fns) {
|
||||
matched = (fn)(event_name, arg);
|
||||
if (matched) break;
|
||||
}
|
||||
if (!matched) {
|
||||
ESP_LOGW(PLAYBACK_TAG, "Failed to match event: %s!", event_name);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
playback_task_handle = NULL;
|
||||
|
||||
stop_playback();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void register_replay_fn(bool (*replay_fn)(const char*, char*)) {
|
||||
replay_fns.push_back(replay_fn);
|
||||
}
|
||||
|
||||
void event_occured(const char* name, const char* arg) {
|
||||
if (state != STATE_RECORDING) return;
|
||||
if (name == nullptr) return;
|
||||
if (recording_stream == nullptr) {
|
||||
ESP_LOGE("state_tracking", "We are in state recording, but recording stream is null");
|
||||
return;
|
||||
}
|
||||
|
||||
arg = (arg == nullptr) ? "" : arg;
|
||||
uint32_t ticks = xTaskGetTickCount() - recording_start_time;
|
||||
fprintf(recording_stream, "%ld,%s:%s\n", ticks, name, (arg == nullptr) ? "" : arg);
|
||||
}
|
||||
|
||||
bool set_recording_source(FILE* stream, bool should_close) {
|
||||
if (state == STATE_RECORDING) return false;
|
||||
|
||||
recording_stream = stream;
|
||||
should_close_recording_stream = should_close;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_playback_source(FILE* stream, bool should_close) {
|
||||
if (state == STATE_PLAYBACK) return false;
|
||||
|
||||
playback_stream = stream;
|
||||
should_close_playback_stream = should_close;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_recording() {
|
||||
if (state != STATE_IDLE) return false;
|
||||
if (recording_stream == nullptr) return false;
|
||||
|
||||
state = STATE_RECORDING;
|
||||
recording_start_time = xTaskGetTickCount();
|
||||
xTaskCreate(flush_file_task, "flush_recording", 2048, NULL, 2, &flush_file_task_handle);
|
||||
reset_wlv_tables(); // TODO: generify this
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stop_recording() {
|
||||
if (state != STATE_RECORDING) return false;
|
||||
|
||||
state = STATE_IDLE;
|
||||
fflush(recording_stream);
|
||||
if (should_close_recording_stream) {
|
||||
fclose(recording_stream);
|
||||
recording_stream = nullptr;
|
||||
}
|
||||
|
||||
if (flush_file_task_handle != nullptr) {
|
||||
vTaskDelete(flush_file_task_handle);
|
||||
flush_file_task_handle = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_playback() {
|
||||
if (state != STATE_IDLE) return false;
|
||||
if (playback_stream == nullptr) return false;
|
||||
|
||||
state = STATE_PLAYBACK;
|
||||
playback_start_time = xTaskGetTickCount();
|
||||
xTaskCreate(playback_task, "playback", 4096, NULL, 2, &playback_task_handle);
|
||||
reset_wlv_tables(); // TODO: generify this
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stop_playback() {
|
||||
if (state != STATE_PLAYBACK) return false;
|
||||
|
||||
state = STATE_IDLE;
|
||||
if (should_close_playback_stream) {
|
||||
fclose(playback_stream);
|
||||
playback_stream = nullptr;
|
||||
}
|
||||
|
||||
if (playback_task_handle != nullptr) {
|
||||
// TODO: NO!! This leaks the malloc. Instead, use a queue to request a graceful stop
|
||||
vTaskDelete(playback_task_handle);
|
||||
playback_task_handle = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_state_tracking() {
|
||||
return state == STATE_RECORDING;
|
||||
}
|
||||
53
main/drivers/state_tracking.h
Normal file
53
main/drivers/state_tracking.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef STATE_TRACKING_H
|
||||
#define STATE_TRACKING_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_vfs_fat.h"
|
||||
|
||||
/// @brief Registers function to be called on replay.
|
||||
/// @param replay_callback A function to call to playback the event.
|
||||
void register_replay_fn(bool (*replay_fn)(const char*, char*));
|
||||
|
||||
// TODO: add one for generically responding to all events.
|
||||
|
||||
/// @brief Call this to indicate that the bomb state has transitioned.
|
||||
/// @param name The name of the event that has occured.
|
||||
/// @param arg The serialized data associated with the event.
|
||||
/// This must not contain newline characters such as '\n' or '\r'
|
||||
void event_occured(const char* name, const char* arg);
|
||||
|
||||
/// @brief Sets the recording source.
|
||||
/// @param stream The stream to record to.
|
||||
/// @param should_close whether or not the stream should be closed when finished.
|
||||
/// @return true if the source was updated.
|
||||
bool set_recording_source(FILE* stream, bool should_close);
|
||||
|
||||
/// @brief Sets the playback source.
|
||||
/// @param stream The stream to playback from.
|
||||
/// @param should_close whether or not the stream should be closed when finished.
|
||||
/// @return true if the source was updated.
|
||||
bool set_playback_source(FILE* stream, bool should_close);
|
||||
|
||||
/// @brief Starts recording to the stream specified by `set_recording_source()`.
|
||||
/// @return true if starting recording is successful.
|
||||
bool start_recording();
|
||||
|
||||
/// @brief Stops recording the state.
|
||||
/// @return true if stopping the recording is successful.
|
||||
bool stop_recording();
|
||||
|
||||
/// @brief Starts playing back the recording specified by `set_playback_source()`.
|
||||
/// @return true if starting playback is successful.
|
||||
bool start_playback();
|
||||
|
||||
/// @brief Stops playing back the recording.
|
||||
/// @return true if stopping the playback is successful.
|
||||
bool stop_playback();
|
||||
|
||||
/// @brief Gets weather or not we are tracking the state
|
||||
/// This can be helpful to conditionally do extra computation only
|
||||
/// when we are tracking the state.
|
||||
/// @return
|
||||
bool is_state_tracking();
|
||||
|
||||
#endif /* STATE_TRACKING_H */
|
||||
234
main/drivers/tft.cpp
Normal file
234
main/drivers/tft.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
#include "tft.h"
|
||||
#include "state_tracking.h"
|
||||
|
||||
static const char* TAG = "tft";
|
||||
|
||||
static esp_lcd_panel_io_handle_t lcd_io_handle = NULL;
|
||||
static esp_lcd_panel_handle_t lcd_handle = NULL;
|
||||
|
||||
static lv_disp_draw_buf_t lv_disp_buf;
|
||||
static lv_disp_drv_t lv_disp_drv;
|
||||
static lv_disp_t *lv_display = NULL;
|
||||
static lv_color_t *lv_buf_1 = NULL;
|
||||
static lv_color_t *lv_buf_2 = NULL;
|
||||
|
||||
lv_obj_t* screen;
|
||||
static lv_style_t style_screen;
|
||||
|
||||
SemaphoreHandle_t xGuiSemaphore;
|
||||
|
||||
static bool replay_handler(const char* event, char* arg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool notify_lvgl_flush_ready(
|
||||
esp_lcd_panel_io_handle_t panel_io,
|
||||
esp_lcd_panel_io_event_data_t *edata,
|
||||
void *user_ctx
|
||||
) {
|
||||
lv_disp_drv_t* disp_driver = (lv_disp_drv_t *)user_ctx;
|
||||
lv_disp_flush_ready(disp_driver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
/// Base 64 encodes a u16... sort of. This doesn't do any of the fancy padding stuff.
|
||||
static void encode_base64(char* buf, size_t start_idx, uint16_t value) {
|
||||
buf[start_idx+0] = base64_chars[(value >> 10) & 0x3F];
|
||||
buf[start_idx+1] = base64_chars[(value >> 4) & 0x3F];
|
||||
buf[start_idx+2] = base64_chars[(value << 2) & 0x3F];
|
||||
}
|
||||
|
||||
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) {
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
|
||||
int offsetx1 = area->x1;
|
||||
int offsetx2 = area->x2;
|
||||
int offsety1 = area->y1;
|
||||
int offsety2 = area->y2;
|
||||
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||
|
||||
// TODO: change this to be a kconfig value
|
||||
#if false
|
||||
|
||||
if (is_state_tracking()) {
|
||||
size_t size = (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1) + 1;
|
||||
// if (size > 1024) {
|
||||
// ESP_LOGW("tft_track_state", "Write too big (%d)! truncating to 1024!", size);
|
||||
// }
|
||||
// size = MIN(1024, size);
|
||||
|
||||
|
||||
// 24 bytes for the offsets
|
||||
// 3 bytes per encoded color
|
||||
// 1 byte for null terminator
|
||||
size_t alloc_size = 24 + size * 3 + 1;
|
||||
char* buf = (char*)malloc(alloc_size);
|
||||
|
||||
if (buf != nullptr) {
|
||||
size_t initial_offset = sprintf(buf, "%d,%d,%d,%d:", offsetx1, offsety1, offsetx2 + 1, offsety2 + 1);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
size_t index = initial_offset + i * 3;
|
||||
|
||||
// we assume that the size of the color data is 16b
|
||||
static_assert(sizeof(lv_color_t) == sizeof(uint16_t), "lv_color_t must be 16b wide");
|
||||
encode_base64(buf, index, color_map[i].full);
|
||||
}
|
||||
buf[initial_offset + (size-1) * 3 + 1] = '\0';
|
||||
|
||||
event_occured("TFT_W", buf);
|
||||
free(buf);
|
||||
} else {
|
||||
ESP_LOGE("tft_track_state", "buffer alloc failed!");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static void IRAM_ATTR lv_tick_task(void *param) {
|
||||
lv_tick_inc(LVGL_UPDATE_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void initialize_spi() {
|
||||
ESP_LOGI(TAG, "Initializing SPI bus (MOSI:%d, MISO:%d, CLK:%d)",
|
||||
TFT_PIN_MOSI, TFT_PIN_MISO, TFT_PIN_CLK);
|
||||
|
||||
spi_bus_config_t bus = {
|
||||
.mosi_io_num = TFT_PIN_MOSI,
|
||||
.miso_io_num = TFT_PIN_MISO,
|
||||
.sclk_io_num = TFT_PIN_CLK,
|
||||
.quadwp_io_num = GPIO_NUM_NC,
|
||||
.quadhd_io_num = GPIO_NUM_NC,
|
||||
.data4_io_num = GPIO_NUM_NC,
|
||||
.data5_io_num = GPIO_NUM_NC,
|
||||
.data6_io_num = GPIO_NUM_NC,
|
||||
.data7_io_num = GPIO_NUM_NC,
|
||||
.max_transfer_sz = SPI_MAX_TRANSFER_SIZE,
|
||||
.flags = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO |
|
||||
SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MASTER,
|
||||
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO,
|
||||
.intr_flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
static void initialize_display() {
|
||||
const esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.cs_gpio_num = TFT_PIN_CS,
|
||||
.dc_gpio_num = TFT_PIN_DC,
|
||||
.spi_mode = 0,
|
||||
.pclk_hz = DISPLAY_REFRESH_HZ,
|
||||
.trans_queue_depth = DISPLAY_SPI_QUEUE_LEN,
|
||||
.on_color_trans_done = notify_lvgl_flush_ready,
|
||||
.user_ctx = &lv_disp_drv,
|
||||
.lcd_cmd_bits = DISPLAY_COMMAND_BITS,
|
||||
.lcd_param_bits = DISPLAY_PARAMETER_BITS,
|
||||
.flags = {
|
||||
.dc_high_on_cmd = 0, /*!< If enabled, DC level = 1 indicates command transfer */
|
||||
.dc_low_on_data = 0, /*!< If enabled, DC level = 0 indicates color data transfer */
|
||||
.dc_low_on_param = 0, /*!< If enabled, DC level = 0 indicates parameter transfer */
|
||||
.octal_mode = 0, /*!< transmit with octal mode (8 data lines), this mode is used to simulate Intel 8080 timing */
|
||||
.quad_mode = 0, /*!< transmit with quad mode (4 data lines), this mode is useful when transmitting LCD parameters (Only use one line for command) */
|
||||
.sio_mode = 0, /*!< Read and write through a single data line (MOSI) */
|
||||
.lsb_first = 0, /*!< transmit LSB bit first */
|
||||
.cs_high_active = 0, /*!< CS line is high active */
|
||||
}
|
||||
};
|
||||
|
||||
const esp_lcd_panel_dev_config_t lcd_config = {
|
||||
.reset_gpio_num = TFT_PIN_RESET,
|
||||
.color_space = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||
.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
|
||||
.bits_per_pixel = 18,
|
||||
.flags = {
|
||||
.reset_active_high = 0
|
||||
},
|
||||
.vendor_config = NULL
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(
|
||||
esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &lcd_io_handle));
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9488(lcd_io_handle, &lcd_config, LV_BUFFER_SIZE, &lcd_handle));
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(lcd_handle, true));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(lcd_handle, true));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(lcd_handle, false, true));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(lcd_handle, 0, 0));
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_off(lcd_handle, false));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(lcd_handle, true));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void guiTask(void *pvParameter) {
|
||||
xGuiSemaphore = xSemaphoreCreateMutex();
|
||||
ESP_LOGI(TAG, "Initializing LVGL");
|
||||
lv_init();
|
||||
ESP_LOGI(TAG, "Allocating %zu bytes for LVGL buffer", LV_BUFFER_SIZE * sizeof(lv_color_t));
|
||||
lv_buf_1 = (lv_color_t *)heap_caps_malloc(LV_BUFFER_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
#if USE_DOUBLE_BUFFERING
|
||||
ESP_LOGI(TAG, "Allocating %zu bytes for second LVGL buffer", LV_BUFFER_SIZE * sizeof(lv_color_t));
|
||||
lv_buf_2 = (lv_color_t *)heap_caps_malloc(LV_BUFFER_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Creating LVLG display buffer");
|
||||
lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, LV_BUFFER_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "Initializing %dx%d display", DISPLAY_HORIZONTAL_PIXELS, DISPLAY_VERTICAL_PIXELS);
|
||||
lv_disp_drv_init(&lv_disp_drv);
|
||||
lv_disp_drv.hor_res = DISPLAY_HORIZONTAL_PIXELS;
|
||||
lv_disp_drv.ver_res = DISPLAY_VERTICAL_PIXELS;
|
||||
lv_disp_drv.flush_cb = lvgl_flush_cb;
|
||||
lv_disp_drv.draw_buf = &lv_disp_buf;
|
||||
lv_disp_drv.user_data = lcd_handle;
|
||||
// lv_disp_drv.rotated = LV_DISP_ROT_90;
|
||||
lv_display = lv_disp_drv_register(&lv_disp_drv);
|
||||
|
||||
ESP_LOGI(TAG, "Creating LVGL tick timer");
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &lv_tick_task,
|
||||
// .dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "periodic_gui",
|
||||
// .skip_unhandled_events = false
|
||||
};
|
||||
esp_timer_handle_t periodic_timer;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &periodic_timer));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LVGL_UPDATE_PERIOD_MS * 1000));
|
||||
|
||||
screen = lv_scr_act();
|
||||
lv_style_init(&style_screen);
|
||||
lv_style_set_bg_color(&style_screen, lv_color_black());
|
||||
lv_obj_add_style(screen, &style_screen, LV_STATE_DEFAULT);
|
||||
|
||||
while (1) {
|
||||
/* Delay 1 tick (assumes FreeRTOS tick is 10ms */
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
/* Try to take the semaphore, call lvgl related function on success */
|
||||
if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
|
||||
lv_task_handler();
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void init_tft() {
|
||||
ESP_LOGI(TAG, "Initializing TFT...");
|
||||
|
||||
initialize_spi();
|
||||
initialize_display();
|
||||
xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 5, NULL, 1);
|
||||
|
||||
register_replay_fn(replay_handler);
|
||||
|
||||
ESP_LOGI(TAG, "TFT initialized!");
|
||||
}
|
||||
72
main/drivers/tft.h
Normal file
72
main/drivers/tft.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef TFT_H
|
||||
#define TFT_H
|
||||
|
||||
/*
|
||||
* Adapted from an example under the MIT license:
|
||||
* Copyright © 2022 atanisoft (github.com/atanisoft)
|
||||
*
|
||||
* MIT LICENSE:
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_freertos_hooks.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include "esp_lcd_ili9488/esp_lcd_ili9488.h"
|
||||
#include <esp_timer.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <lvgl.h>
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
// Uncomment the following line to enable using double buffering of LVGL color
|
||||
// data.
|
||||
// #define USE_DOUBLE_BUFFERING 1
|
||||
|
||||
// rotation swaps the horizontal and vertical pixel counts
|
||||
#define DISPLAY_HORIZONTAL_PIXELS 480
|
||||
#define DISPLAY_VERTICAL_PIXELS 320
|
||||
#define DISPLAY_COMMAND_BITS 8
|
||||
#define DISPLAY_PARAMETER_BITS 8
|
||||
#define DISPLAY_REFRESH_HZ 40000000
|
||||
#define DISPLAY_SPI_QUEUE_LEN 10
|
||||
#define SPI_MAX_TRANSFER_SIZE 32768
|
||||
|
||||
#define TFT_PIN_MOSI GPIO_NUM_17
|
||||
#define TFT_PIN_MISO GPIO_NUM_16
|
||||
#define TFT_PIN_CLK GPIO_NUM_18
|
||||
#define TFT_PIN_CS GPIO_NUM_NC
|
||||
#define TFT_PIN_DC GPIO_NUM_8
|
||||
#define TFT_PIN_RESET GPIO_NUM_9
|
||||
|
||||
#define TFT_INVERT_COLOR false
|
||||
|
||||
// Default to 50 lines of color data
|
||||
#define LV_BUFFER_SIZE DISPLAY_HORIZONTAL_PIXELS * 50
|
||||
#define LVGL_UPDATE_PERIOD_MS 5
|
||||
|
||||
#define BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE
|
||||
#define BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0
|
||||
#define BACKLIGHT_LEDC_TIMER LEDC_TIMER_1
|
||||
#define BACKLIGHT_LEDC_TIMER_RESOLUTION LEDC_TIMER_10_BIT
|
||||
#define BACKLIGHT_LEDC_FRQUENCY 5000
|
||||
|
||||
extern lv_obj_t* screen;
|
||||
|
||||
extern SemaphoreHandle_t xGuiSemaphore;
|
||||
|
||||
void create_demo_ui();
|
||||
void init_tft();
|
||||
|
||||
#endif /* TFT_H */
|
||||
169
main/drivers/wires.cpp
Normal file
169
main/drivers/wires.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include "wires.h"
|
||||
|
||||
extern uint32_t current_step;
|
||||
|
||||
/// The mutex for accessing `I2C_NUM_1` (wires I2C bus).
|
||||
SemaphoreHandle_t wires_i2c_mutex;
|
||||
|
||||
uint32_t total_strikes;
|
||||
uint32_t step_strikes[N_STEPS] = {0};
|
||||
uint32_t step_finish_times[N_STEPS] = {0};
|
||||
|
||||
static const char *TAG = "wires";
|
||||
|
||||
const uint32_t STRIKE_TIME_PENALTY = 30'000;
|
||||
|
||||
static bool button_state;
|
||||
static bool button_pressed;
|
||||
static bool button_released;
|
||||
|
||||
static uint8_t wires_state;
|
||||
static uint8_t wires_cut;
|
||||
|
||||
static uint8_t buf[8];
|
||||
|
||||
static void poll_wires_task(void *arg);
|
||||
static void receive_wires(void);
|
||||
static void receive_button(void);
|
||||
|
||||
void init_wires(void) {
|
||||
i2c_config_t wires_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = PIN_WIRES_SDA,
|
||||
.scl_io_num = PIN_WIRES_SCL,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master = {
|
||||
.clk_speed = 100000,
|
||||
},
|
||||
};
|
||||
|
||||
gpio_reset_pin(PIN_WIRES_SDA);
|
||||
gpio_reset_pin(PIN_WIRES_SCL);
|
||||
|
||||
ESP_ERROR_CHECK(i2c_param_config(WIRES_I2C_NUM, &wires_conf));
|
||||
ESP_ERROR_CHECK(i2c_driver_install(WIRES_I2C_NUM, wires_conf.mode, 0, 0, 0));
|
||||
|
||||
// Create mutex for wires I2C bus
|
||||
wires_i2c_mutex = xSemaphoreCreateMutex();
|
||||
assert(wires_i2c_mutex != NULL);
|
||||
|
||||
gpio_config_t int_pin_conf = {};
|
||||
// delta_pin_conf.intr_type = GPIO_INTR_LOW_LEVEL;
|
||||
int_pin_conf.mode = GPIO_MODE_INPUT;
|
||||
int_pin_conf.pin_bit_mask = (1ULL<<WIRES_PIN_DELTA);
|
||||
int_pin_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
gpio_config(&int_pin_conf);
|
||||
|
||||
receive_button();
|
||||
receive_wires();
|
||||
|
||||
xTaskCreate(poll_wires_task, "poll_wires", 4096, NULL, 10, NULL);
|
||||
}
|
||||
|
||||
uint8_t get_wires(void) {
|
||||
return wires_state;
|
||||
}
|
||||
uint8_t get_cut_wires(void) {
|
||||
uint8_t return_ = wires_cut;
|
||||
wires_cut = 0;
|
||||
return return_;
|
||||
}
|
||||
|
||||
bool get_help_button(void) {
|
||||
return button_state;
|
||||
}
|
||||
bool get_help_button_pressed(void) {
|
||||
bool return_ = button_pressed;
|
||||
button_pressed = false;
|
||||
return return_;
|
||||
}
|
||||
bool get_help_button_released(void) {
|
||||
bool return_ = button_released;
|
||||
button_released = false;
|
||||
return return_;
|
||||
}
|
||||
|
||||
void clear_wires_pressed_released_cut(void) {
|
||||
wires_cut = 0;
|
||||
button_pressed = false;
|
||||
button_released = false;
|
||||
}
|
||||
|
||||
void strike(const char* reason) {
|
||||
ESP_LOGW("strike!", "%s", reason);
|
||||
lcd_print(3, 0, reason);
|
||||
time_penalty(STRIKE_TIME_PENALTY);
|
||||
if (current_step > 0 && current_step <= N_STEPS) {
|
||||
total_strikes += 1;
|
||||
step_strikes[current_step - 1] += 1;
|
||||
}
|
||||
uint8_t reg = 6;
|
||||
xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_to_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(wires_i2c_mutex);
|
||||
}
|
||||
|
||||
void set_leds(uint8_t led_states) {
|
||||
buf[0] = 5; // register 5
|
||||
buf[1] = led_states;
|
||||
xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_to_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, buf, 2, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(wires_i2c_mutex);
|
||||
}
|
||||
|
||||
static uint8_t receive_delta(void) {
|
||||
uint8_t reg = 1;
|
||||
buf[0] = 0;
|
||||
xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(wires_i2c_mutex);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void receive_wires(void) {
|
||||
uint8_t reg = 2;
|
||||
buf[0] = 0;
|
||||
xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(wires_i2c_mutex);
|
||||
uint8_t new_wires = buf[0];
|
||||
|
||||
uint8_t just_cut = ~new_wires & wires_state;
|
||||
wires_cut |= just_cut;
|
||||
|
||||
wires_state = new_wires;
|
||||
}
|
||||
|
||||
static void receive_button(void) {
|
||||
uint8_t reg = 3;
|
||||
buf[0] = 0;
|
||||
xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS)));
|
||||
xSemaphoreGive(wires_i2c_mutex);
|
||||
bool new_button = buf[0] != 0;
|
||||
|
||||
bool just_pressed = new_button & !button_state;
|
||||
button_pressed |= just_pressed;
|
||||
|
||||
bool just_released = !new_button & button_state;
|
||||
button_released |= just_released;
|
||||
|
||||
button_state = new_button;
|
||||
}
|
||||
|
||||
static void poll_wires_task(void *arg) {
|
||||
while (1) {
|
||||
bool new_data = gpio_get_level(WIRES_PIN_DELTA) == 0;
|
||||
if (new_data) {
|
||||
uint8_t delta = receive_delta();
|
||||
if (delta == 0) ESP_LOGW(TAG, "delta pin was low, but delta register returned 0");
|
||||
|
||||
if (delta & (1 << DELTA_BIT_WIRES)) receive_wires();
|
||||
if (delta & (1 << DELTA_BIT_BUTTON)) receive_button();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
47
main/drivers/wires.h
Normal file
47
main/drivers/wires.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef WIRES_HPP
|
||||
#define WIRES_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "drivers/char_lcd.h"
|
||||
#include "drivers/game_timer.h"
|
||||
#include "main.h"
|
||||
#include "perh.h"
|
||||
|
||||
#define WIRES_PIN_DELTA PIN_PERH3
|
||||
#define PIN_WIRES_SDA PIN_PERH1
|
||||
#define PIN_WIRES_SCL PIN_PERH2
|
||||
#define WIRES_I2C_NUM I2C_NUM_1
|
||||
#define WIRES_I2C_ADDR 125
|
||||
|
||||
/// The mutex for accessing `I2C_NUM_1` (wires I2C bus).
|
||||
extern SemaphoreHandle_t wires_i2c_mutex;
|
||||
|
||||
#define DELTA_BIT_WIRES 0
|
||||
#define DELTA_BIT_BUTTON 1
|
||||
|
||||
extern const uint32_t STRIKE_TIME_PENALTY;
|
||||
extern uint32_t step_strikes[N_STEPS];
|
||||
extern uint32_t step_finish_times[N_STEPS];
|
||||
|
||||
extern uint32_t total_strikes;
|
||||
|
||||
void init_wires(void);
|
||||
uint8_t get_wires(void);
|
||||
uint8_t get_cut_wires(void);
|
||||
|
||||
bool get_help_button(void);
|
||||
bool get_help_button_pressed(void);
|
||||
bool get_help_button_released(void);
|
||||
|
||||
void clear_wires_pressed_released_cut(void);
|
||||
|
||||
void set_leds(uint8_t led_states);
|
||||
|
||||
void strike(const char* reason);
|
||||
|
||||
#endif /* WIRES_HPP */
|
||||
152
main/drivers/wlvgl.cpp
Normal file
152
main/drivers/wlvgl.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include "wlvgl.h"
|
||||
#include <vector>
|
||||
|
||||
/// A table that maps an incrementing integer to a style reference
|
||||
static std::vector<lv_style_t*> style_table;
|
||||
|
||||
/// A table that maps an incrementing integer to a style reference
|
||||
static std::vector<lv_obj_t*> obj_table;
|
||||
|
||||
static int index_of_style(lv_style_t* style) {
|
||||
for (size_t i = 0; i < style_table.size(); i++) {
|
||||
if (style_table[i] == style) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int index_of_obj(lv_obj_t* obj) {
|
||||
for (size_t i = 0; i < obj_table.size(); i++) {
|
||||
if (obj_table[i] == obj) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void reset_wlv_tables() {
|
||||
style_table.clear();
|
||||
obj_table.clear();
|
||||
}
|
||||
|
||||
lv_obj_t * wlv_obj_create(lv_obj_t* parent) {
|
||||
// initialize the obj
|
||||
lv_obj_t* value = lv_obj_create(parent);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
// add the style to the table
|
||||
obj_table.push_back(value);
|
||||
size_t obj_index = obj_table.size();
|
||||
|
||||
char buf[8];
|
||||
sprintf(buf, "%d", obj_index);
|
||||
event_occured("lv_obj_create", buf);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void wlv_style_init(lv_style_t* style) {
|
||||
// initialize the style
|
||||
lv_style_init(style);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
// add the style to the table
|
||||
style_table.push_back(style);
|
||||
size_t style_index = style_table.size();
|
||||
|
||||
char buf[8];
|
||||
sprintf(buf, "%d", style_index);
|
||||
event_occured("lv_style_init", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void wlv_obj_set_style_bg_color(lv_obj_t* obj, lv_color_t value, lv_style_selector_t selector) {
|
||||
lv_obj_set_style_bg_color(obj, value, selector);
|
||||
|
||||
if (is_state_tracking()) {
|
||||
int obj_index = index_of_obj(obj);
|
||||
char buf[64];
|
||||
sprintf(buf, "%d,%d,%ld", obj_index, value.full, selector);
|
||||
event_occured("lv_obj_set_style_bg_color", buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wlv_style_set_text_color() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_style_set_text_align() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_align() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_set_size() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_add_style() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_set_style_text_align() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_label_set_text() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_scr_load() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_scr_act() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_style_set_bg_color() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_style_set_bg_opa() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_set_width() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_style_set_text_font() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_label_create() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_del() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_center() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_remove_style() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_add_flag() {
|
||||
|
||||
}
|
||||
|
||||
void wlv_obj_clear_flag() {
|
||||
|
||||
}
|
||||
|
||||
32
main/drivers/wlvgl.h
Normal file
32
main/drivers/wlvgl.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef WLVGL_H
|
||||
#define WLVGL_H
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "state_tracking.h"
|
||||
|
||||
void reset_wlv_tables();
|
||||
|
||||
lv_obj_t* wlv_obj_create(lv_obj_t* parent);
|
||||
void wlv_style_init(lv_style_t* style);
|
||||
void wlv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
|
||||
void wlv_style_set_text_color();
|
||||
void wlv_style_set_text_align();
|
||||
void wlv_obj_align();
|
||||
void wlv_obj_set_size();
|
||||
void wlv_obj_add_style();
|
||||
void wlv_obj_set_style_text_align();
|
||||
void wlv_label_set_text();
|
||||
void wlv_scr_load();
|
||||
void wlv_scr_act();
|
||||
void wlv_style_set_bg_color();
|
||||
void wlv_style_set_bg_opa();
|
||||
void wlv_obj_set_width();
|
||||
void wlv_style_set_text_font();
|
||||
void wlv_label_create();
|
||||
void wlv_obj_del();
|
||||
void wlv_obj_center();
|
||||
void wlv_obj_remove_style();
|
||||
void wlv_obj_add_flag();
|
||||
void wlv_obj_clear_flag();
|
||||
|
||||
#endif /* WLVGL_H */
|
||||
127
main/helper.cpp
Normal file
127
main/helper.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "helper.h"
|
||||
|
||||
// TODO: move to driver
|
||||
void clean_bomb(void) {
|
||||
// clear pending inputs
|
||||
clear_all_pressed_released();
|
||||
clear_wires_pressed_released_cut();
|
||||
|
||||
// clear leds
|
||||
leds_clear();
|
||||
leds_flush();
|
||||
|
||||
// clear module timer
|
||||
stop_module_timer();
|
||||
set_module_time(0);
|
||||
uint8_t clear[4] = {0};
|
||||
set_module_sseg_raw(clear);
|
||||
|
||||
// clear char lcd
|
||||
lcd_set_cursor_vis(false);
|
||||
lcd_clear();
|
||||
|
||||
// TODO: add stuff for starcode system
|
||||
}
|
||||
|
||||
static lv_obj_t* old_scr;
|
||||
static lv_obj_t* scr;
|
||||
|
||||
static lv_style_t game_results_style;
|
||||
static lv_obj_t* overall_results_label;
|
||||
static lv_obj_t* breakdown_results_label;
|
||||
static lv_obj_t* strike_numbers_label;
|
||||
static lv_obj_t* step_times_label;
|
||||
|
||||
static void write_time(char* buf, int32_t game_time) {
|
||||
char minus[2] = "\0";
|
||||
if (game_time < 0) {
|
||||
minus[0] = '-';
|
||||
game_time = -game_time;
|
||||
}
|
||||
|
||||
uint8_t hours = (game_time / (1000*60*60)) % 10;
|
||||
uint8_t minutes = (game_time / (1000*60)) % 60;
|
||||
uint8_t seconds = (game_time / (1000)) % 60;
|
||||
|
||||
sprintf(buf, "%s%d:%02d:%02d", minus, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
extern uint32_t initial_game_time;
|
||||
void display_game_results(void) {
|
||||
while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
scr = lv_obj_create(NULL);
|
||||
lv_obj_set_style_bg_color(scr, lv_color_black(), LV_STATE_DEFAULT);
|
||||
|
||||
lv_style_init(&game_results_style);
|
||||
lv_style_set_text_color(&game_results_style, lv_color_white());
|
||||
lv_style_set_text_align(&game_results_style, LV_TEXT_ALIGN_LEFT);
|
||||
|
||||
overall_results_label = lv_label_create(scr);
|
||||
lv_obj_align(overall_results_label, LV_ALIGN_TOP_LEFT, 20, 20);
|
||||
lv_obj_set_size(overall_results_label, 460, 140);
|
||||
lv_obj_add_style(overall_results_label, &game_results_style, LV_STATE_DEFAULT);
|
||||
|
||||
breakdown_results_label = lv_label_create(scr);
|
||||
lv_obj_align(breakdown_results_label, LV_ALIGN_BOTTOM_LEFT, 20, 0);
|
||||
lv_obj_set_size(breakdown_results_label, 460, 140);
|
||||
lv_obj_add_style(breakdown_results_label, &game_results_style, LV_STATE_DEFAULT);
|
||||
|
||||
strike_numbers_label = lv_label_create(scr);
|
||||
lv_obj_align(strike_numbers_label, LV_ALIGN_BOTTOM_LEFT, 90, 0);
|
||||
lv_obj_set_size(strike_numbers_label, 60, 140);
|
||||
lv_obj_add_style(strike_numbers_label, &game_results_style, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_align(strike_numbers_label, LV_TEXT_ALIGN_RIGHT, LV_STATE_DEFAULT);
|
||||
|
||||
step_times_label = lv_label_create(scr);
|
||||
lv_obj_align(step_times_label, LV_ALIGN_BOTTOM_LEFT, 140, 0);
|
||||
lv_obj_set_size(step_times_label, 80, 140);
|
||||
lv_obj_add_style(step_times_label, &game_results_style, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_align(step_times_label, LV_TEXT_ALIGN_RIGHT, LV_STATE_DEFAULT);
|
||||
|
||||
char buf[1024] = {0};
|
||||
|
||||
char time_buf_1[32] = {0};
|
||||
char time_buf_2[32] = {0};
|
||||
char time_buf_3[32] = {0};
|
||||
char time_buf_4[32] = {0};
|
||||
char time_buf_5[32] = {0};
|
||||
char time_buf_6[32] = {0};
|
||||
|
||||
int32_t time_left = get_game_time();
|
||||
int32_t time_spent = initial_game_time - time_left;
|
||||
int32_t true_time_spent = time_spent - (total_strikes * STRIKE_TIME_PENALTY);
|
||||
write_time(time_buf_1, time_left);
|
||||
write_time(time_buf_2, time_spent);
|
||||
write_time(time_buf_3, true_time_spent);
|
||||
|
||||
sprintf(buf,
|
||||
"Finished!\n\tTotal Strikes: %ld\n\tTime Left (w/ penalties): %s\n\tTime Spent (w/ penalties): %s\n\tTrue Time Spent: %s\n\n",
|
||||
total_strikes, time_buf_1, time_buf_2, time_buf_3
|
||||
);
|
||||
lv_label_set_text(overall_results_label, buf);
|
||||
|
||||
lv_label_set_text(breakdown_results_label, "Step Breakdown:\n\tStep 1: \n\tStep 2:\n\tStep 3:\n\tStep 4:\n\tStep 5:\n\tStep 6:");
|
||||
|
||||
sprintf(buf,
|
||||
"\n%ld strikes\n%ld strikes\n%ld strikes\n%ld strikes\n%ld strikes\n%ld strikes",
|
||||
step_strikes[0], step_strikes[1], step_strikes[2], step_strikes[3], step_strikes[4], step_strikes[5]
|
||||
);
|
||||
lv_label_set_text(strike_numbers_label, buf);
|
||||
|
||||
write_time(time_buf_1, initial_game_time - step_finish_times[0]);
|
||||
write_time(time_buf_2, step_finish_times[0] - step_finish_times[1]);
|
||||
write_time(time_buf_3, step_finish_times[1] - step_finish_times[2]);
|
||||
write_time(time_buf_4, step_finish_times[2] - step_finish_times[3]);
|
||||
write_time(time_buf_5, step_finish_times[3] - step_finish_times[4]);
|
||||
write_time(time_buf_6, step_finish_times[4] - step_finish_times[5]);
|
||||
sprintf(buf,
|
||||
"\n%s\n%s\n%s\n%s\n%s\n%s",
|
||||
time_buf_1, time_buf_2, time_buf_3, time_buf_4, time_buf_5, time_buf_6
|
||||
);
|
||||
lv_label_set_text(step_times_label, buf);
|
||||
|
||||
old_scr = lv_scr_act();
|
||||
lv_scr_load(scr);
|
||||
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
20
main/helper.h
Normal file
20
main/helper.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef HELPER_H
|
||||
#define HELPER_H
|
||||
|
||||
#include "drivers/game_timer.h"
|
||||
#include "drivers/sseg.h"
|
||||
#include "drivers/bottom_half.h"
|
||||
#include "drivers/wires.h"
|
||||
#include "drivers/char_lcd.h"
|
||||
#include "drivers/leds.h"
|
||||
#include "drivers/speaker.h"
|
||||
#include "drivers/tft.h"
|
||||
|
||||
// TODO: add something for RNG.
|
||||
|
||||
/// Clears most persistant bomb state
|
||||
void clean_bomb(void);
|
||||
void poster_child_task(void* arg);
|
||||
void display_game_results();
|
||||
|
||||
#endif /* HELPER_H */
|
||||
20
main/idf_component.yml
Normal file
20
main/idf_component.yml
Normal file
@ -0,0 +1,20 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
# iamflinks/i2c_lcd_pcf8574: "^1.0.1"
|
||||
espressif/led_strip: "^2.5.4"
|
||||
lvgl/lvgl: "^8.1"
|
||||
# atanisoft/esp_lcd_ili9488: "^1.0.9"
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
# # Put list of dependencies here
|
||||
# # For components maintained by Espressif:
|
||||
# component: "~1.0.0"
|
||||
# # For 3rd party components:
|
||||
# username/component: ">=1.0.0,<2.0.0"
|
||||
# username2/component2:
|
||||
# version: "~1.0.0"
|
||||
# # For transient dependencies `public` flag can be set.
|
||||
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||
# # All dependencies of `main` are public by default.
|
||||
# public: true
|
||||
51
main/main.c
Normal file
51
main/main.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
void app_main(void) {
|
||||
printf("Hello world!\n");
|
||||
|
||||
/* Print chip information */
|
||||
esp_chip_info_t chip_info;
|
||||
uint32_t flash_size;
|
||||
esp_chip_info(&chip_info);
|
||||
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
|
||||
CONFIG_IDF_TARGET,
|
||||
chip_info.cores,
|
||||
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
|
||||
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
|
||||
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
|
||||
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
|
||||
|
||||
unsigned major_rev = chip_info.revision / 100;
|
||||
unsigned minor_rev = chip_info.revision % 100;
|
||||
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
|
||||
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
|
||||
printf("Get flash size failed");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
|
||||
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
|
||||
|
||||
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
|
||||
|
||||
for (int i = 10; i >= 0; i--) {
|
||||
printf("Restarting in %d seconds...\n", i);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
printf("Restarting now.\n");
|
||||
fflush(stdout);
|
||||
esp_restart();
|
||||
}
|
||||
88
main/main.cpp
Executable file
88
main/main.cpp
Executable file
@ -0,0 +1,88 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#include "drivers/all.h"
|
||||
#include "drivers/state_tracking.h"
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
#include "steps/step0.h"
|
||||
#include "steps/step1.h"
|
||||
#include "steps/step2.h"
|
||||
#include "steps/step3.h"
|
||||
#include "steps/step4.h"
|
||||
#include "steps/step5.h"
|
||||
#include "steps/step6.h"
|
||||
|
||||
static const char *TAG = "main";
|
||||
uint32_t initial_game_time = 90*60*1000 + 1000;
|
||||
uint32_t skip_to_step = 0;
|
||||
uint32_t current_step = 0;
|
||||
extern uint32_t total_strikes;
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
printf("app_main\n");
|
||||
|
||||
init_drivers();
|
||||
|
||||
// TODO: get wires out of the drivers
|
||||
// TODO: generify the strike system out of wires
|
||||
init_wires();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
clean_bomb();
|
||||
lcd_do_splash();
|
||||
step0();
|
||||
|
||||
// set_recording_source(stdout, false);
|
||||
FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w");
|
||||
if (record_file == nullptr) {
|
||||
ESP_LOGE("main", "failed to open record.txt");
|
||||
}
|
||||
set_recording_source(record_file, true);
|
||||
start_recording();
|
||||
|
||||
set_game_time(initial_game_time);
|
||||
start_game_timer();
|
||||
total_strikes = 0;
|
||||
clean_bomb();
|
||||
current_step = 1;
|
||||
if (skip_to_step <= 1) step1();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
current_step = 2;
|
||||
if (skip_to_step <= 2) step2();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
current_step = 3;
|
||||
if (skip_to_step <= 3) step3();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
current_step = 4;
|
||||
if (skip_to_step <= 4) step4();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
current_step = 5;
|
||||
if (skip_to_step <= 5) step5();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
current_step = 6;
|
||||
if (skip_to_step <= 6) step6();
|
||||
start_game_timer();
|
||||
step_finish_times[current_step-1] = get_game_time();
|
||||
clean_bomb();
|
||||
|
||||
stop_game_timer();
|
||||
ESP_LOGI(TAG, "Bomb has been diffused. Counter-Terrorists win.");
|
||||
play_clip_wav(MOUNT_POINT "/diffuse.wav", true, false, 3, 0);
|
||||
|
||||
display_game_results();
|
||||
stop_recording();
|
||||
|
||||
}
|
||||
8
main/main.h
Normal file
8
main/main.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
constexpr size_t N_STEPS = 6;
|
||||
|
||||
#endif /* MAIN_H */
|
||||
15
main/steps/CMakeLists.txt
Normal file
15
main/steps/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
set(SOURCES
|
||||
"setup_wires.cpp"
|
||||
"step0.cpp"
|
||||
"step1.cpp"
|
||||
"step2.cpp"
|
||||
"step3.cpp"
|
||||
"step4.cpp"
|
||||
"step5.cpp"
|
||||
"step6.cpp"
|
||||
"wires_puzzle.cpp"
|
||||
)
|
||||
|
||||
add_subdirectory(tetris_resources)
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES})
|
||||
83
main/steps/setup_wires.cpp
Normal file
83
main/steps/setup_wires.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "setup_wires.h"
|
||||
|
||||
static uint8_t wires_state = 0;
|
||||
|
||||
void print_wires(WireColor* wires, int editing_idx) {
|
||||
bool cut[NUM_WIRES];
|
||||
solve_wires(wires, cut);
|
||||
|
||||
char string_buf[NUM_WIRES+1] = {0};
|
||||
wires_to_string(wires, string_buf);
|
||||
lcd_print(1, 1, string_buf);
|
||||
|
||||
cut_to_string(cut, string_buf);
|
||||
lcd_print(2, 1, string_buf);
|
||||
|
||||
wires_state = get_wires();
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (wires_state & (1<<i)) {
|
||||
string_buf[i] = ' ';
|
||||
} else {
|
||||
string_buf[i] = '!';
|
||||
}
|
||||
}
|
||||
lcd_print(3, 1, string_buf);
|
||||
|
||||
lcd_set_cursor_vis(true);
|
||||
lcd_set_cursor_resting_position(1, editing_idx+1);
|
||||
}
|
||||
|
||||
void setup_wires(void) {
|
||||
clear_all_pressed_released();
|
||||
get_cut_wires();
|
||||
lcd_clear();
|
||||
lcd_set_cursor_vis(true);
|
||||
|
||||
WireColor wires[NUM_WIRES];
|
||||
load_wires_from_sd_card(wires);
|
||||
|
||||
int editing_idx = 0;
|
||||
print_wires(wires, editing_idx);
|
||||
|
||||
KeypadKey key;
|
||||
ButtonKey button;
|
||||
|
||||
while (1) {
|
||||
while (get_keypad_pressed(&key)) {
|
||||
if (key == KeypadKey::k1) {
|
||||
generate_new_wires(wires);
|
||||
save_wires_to_sd_card(wires);
|
||||
print_wires(wires, editing_idx);
|
||||
} else if (key == KeypadKey::pound) {
|
||||
lcd_set_cursor_vis(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (get_button_pressed(&button)) {
|
||||
if (button == ButtonKey::b1) {
|
||||
// left
|
||||
editing_idx = editing_idx - 1;
|
||||
if (editing_idx < 0) editing_idx = 0;
|
||||
} else if (button == ButtonKey::b4) {
|
||||
// right
|
||||
editing_idx = editing_idx + 1;
|
||||
if (editing_idx >= NUM_WIRES) editing_idx = NUM_WIRES-1;
|
||||
} else if (button == ButtonKey::b2) {
|
||||
// down
|
||||
wires[editing_idx] = (WireColor)(((int)(wires[editing_idx]) + (NUM_COLORS-1)) % NUM_COLORS);
|
||||
} else if (button == ButtonKey::b3) {
|
||||
// up
|
||||
wires[editing_idx] = (WireColor)(((int)(wires[editing_idx]) + 1) % NUM_COLORS);
|
||||
}
|
||||
save_wires_to_sd_card(wires);
|
||||
print_wires(wires, editing_idx);
|
||||
}
|
||||
|
||||
uint8_t new_wires_state = get_wires();
|
||||
if (new_wires_state != wires_state) {
|
||||
print_wires(wires, editing_idx);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
12
main/steps/setup_wires.h
Normal file
12
main/steps/setup_wires.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef SETUP_WIRES_H
|
||||
#define SETUP_WIRES_H
|
||||
|
||||
#include "../drivers/bottom_half.h"
|
||||
#include "../drivers/char_lcd.h"
|
||||
#include "../drivers/wires.h"
|
||||
#include "wires_puzzle.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
void setup_wires(void);
|
||||
|
||||
#endif /* SETUP_WIRES_H */
|
||||
425
main/steps/step0.cpp
Normal file
425
main/steps/step0.cpp
Normal file
@ -0,0 +1,425 @@
|
||||
#include "step0.h"
|
||||
#include "drivers/state_tracking.h"
|
||||
|
||||
static const char* TAG = "step0";
|
||||
|
||||
extern uint32_t initial_game_time;
|
||||
extern uint32_t skip_to_step;
|
||||
|
||||
static void set_game_time();
|
||||
static void skip_to_step1() { skip_to_step = 1; }
|
||||
static void skip_to_step2() { skip_to_step = 2; }
|
||||
static void skip_to_step3() { skip_to_step = 3; }
|
||||
static void skip_to_step4() { skip_to_step = 4; }
|
||||
static void skip_to_step5() { skip_to_step = 5; }
|
||||
static void skip_to_step6() { skip_to_step = 6; }
|
||||
static void try_step1() { clean_bomb(); step1(); }
|
||||
static void try_step2() { clean_bomb(); step2(); }
|
||||
static void try_step3() { clean_bomb(); step3(); }
|
||||
static void try_step4() { clean_bomb(); step4(); }
|
||||
static void try_step5() { clean_bomb(); step5(); }
|
||||
static void try_step6() { clean_bomb(); step6(); }
|
||||
static void issue_strike() { strike("Strike Issued"); }
|
||||
static void flashbang();
|
||||
static void debug_switches();
|
||||
static void battery_stats() {
|
||||
BaseType_t xReturned;
|
||||
TaskHandle_t xHandle = NULL;
|
||||
xReturned = xTaskCreate(bat_monitor_task, "bat_monitor", 4096, NULL, 5, &xHandle);
|
||||
|
||||
KeypadKey k;
|
||||
while (!get_keypad_pressed(&k) || k != KeypadKey::pound) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
if (xReturned == pdPASS) {
|
||||
vTaskDelete(xHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove. This is temperary
|
||||
static void replay_last() {
|
||||
FILE* record_file = fopen(MOUNT_POINT "/record.txt", "r");
|
||||
if (record_file == nullptr) {
|
||||
ESP_LOGE("main", "failed to open record.txt");
|
||||
}
|
||||
set_playback_source(record_file, true);
|
||||
start_playback();
|
||||
}
|
||||
|
||||
|
||||
void step0() {
|
||||
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
|
||||
leds_flush();
|
||||
|
||||
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||
if (continue_sem == nullptr) {
|
||||
ESP_LOGE(TAG, "could not create semaphore");
|
||||
return;
|
||||
}
|
||||
|
||||
StarCodeEntry star_codes[] = {
|
||||
{
|
||||
.code = "9819",
|
||||
.display_text = "Diffusal Initiated",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = nullptr,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59860",
|
||||
.display_text = "Hardware Config",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = hardware_config,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59861",
|
||||
.display_text = "Setup Wires",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = setup_wires,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59862",
|
||||
.display_text = "Set Game Time",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = set_game_time,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59863",
|
||||
.display_text = "Debug switches",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = debug_switches,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59864",
|
||||
.display_text = "Battery Stats",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = battery_stats,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59871",
|
||||
.display_text = "Try Step 1",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step1,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59872",
|
||||
.display_text = "Try Step 2",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step2,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59873",
|
||||
.display_text = "Try Step 3",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step3,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59874",
|
||||
.display_text = "Try Step 4",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step4,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59875",
|
||||
.display_text = "Try Step 5",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step5,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59876",
|
||||
.display_text = "Try Step 6",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = try_step6,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "59881",
|
||||
.display_text = "Skip To Step 1",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step1,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59882",
|
||||
.display_text = "Skip To Step 2",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step2,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59883",
|
||||
.display_text = "Skip To Step 3",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step3,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59884",
|
||||
.display_text = "Skip To Step 4",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step4,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59885",
|
||||
.display_text = "Skip To Step 5",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step5,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "59886",
|
||||
.display_text = "Skip To Step 6",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = skip_to_step6,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
{
|
||||
.code = "1111",
|
||||
.display_text = "Issue Strike",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = issue_strike,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "1112",
|
||||
.display_text = "????",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = flashbang,
|
||||
.triggered_sem = nullptr,
|
||||
},
|
||||
{
|
||||
.code = "1113",
|
||||
.display_text = "replay",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = replay_last,
|
||||
.triggered_sem = continue_sem,
|
||||
},
|
||||
};
|
||||
size_t len = sizeof(star_codes)/sizeof(star_codes[0]);
|
||||
|
||||
add_star_codes(star_codes, len);
|
||||
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||
rm_star_codes(star_codes, len);
|
||||
vSemaphoreDelete(continue_sem);
|
||||
}
|
||||
|
||||
static const int CURSOR_POS_MAP[5] = {1, 3, 4, 6, 7};
|
||||
static char str_buf[18] = {0};
|
||||
static void _update_display(uint8_t* digits, uint8_t cursor_pos) {
|
||||
sprintf(str_buf, "%d:%d%d:%d%d", digits[0], digits[1], digits[2], digits[3], digits[4]);
|
||||
lcd_clear();
|
||||
lcd_print(1, 1, str_buf);
|
||||
cursor_pos = MAX(0, MIN(4, cursor_pos));
|
||||
int mapped_cursor_pos = CURSOR_POS_MAP[cursor_pos];
|
||||
|
||||
lcd_set_cursor_resting_position(1, mapped_cursor_pos);
|
||||
}
|
||||
|
||||
static void set_game_time() {
|
||||
uint8_t hours = (initial_game_time / (1000*60*60)) % 10;
|
||||
uint8_t minutes = (initial_game_time / (1000*60)) % 60;
|
||||
uint8_t seconds = (initial_game_time / (1000)) % 60;
|
||||
uint8_t digits[5] = {hours, (uint8_t)(minutes / 10), (uint8_t)(minutes % 10), (uint8_t)(seconds / 10), (uint8_t)(seconds % 10)};
|
||||
uint8_t cursor_pos = 0;
|
||||
lcd_set_cursor_vis(true);
|
||||
|
||||
_update_display(digits, cursor_pos);
|
||||
|
||||
KeypadKey key;
|
||||
ButtonKey button;
|
||||
while (1) {
|
||||
while (get_keypad_pressed(&key)) {
|
||||
if (key == KeypadKey::star) {
|
||||
digits[0] = 0;
|
||||
digits[1] = 0;
|
||||
digits[2] = 0;
|
||||
digits[3] = 0;
|
||||
digits[4] = 0;
|
||||
cursor_pos = 0;
|
||||
} else if (key == KeypadKey::pound) {
|
||||
// submit
|
||||
if (digits[0] != 0 || digits[1] != 0 || digits[2] != 0 || digits[3] != 0 || digits[4] != 0) {
|
||||
uint32_t new_game_time = 0;
|
||||
new_game_time += digits[0] * (1000*60*60);
|
||||
new_game_time += (digits[1] * 10 + digits[2]) * (1000*60);
|
||||
new_game_time += (digits[3] * 10 + digits[4]) * (1000);
|
||||
initial_game_time = new_game_time;
|
||||
}
|
||||
|
||||
clean_bomb();
|
||||
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
|
||||
leds_flush();
|
||||
return;
|
||||
} else {
|
||||
int just_pressed = -1;
|
||||
switch (key) {
|
||||
case KeypadKey::k0:
|
||||
just_pressed = 0;
|
||||
break;
|
||||
case KeypadKey::k1:
|
||||
just_pressed = 1;
|
||||
break;
|
||||
case KeypadKey::k2:
|
||||
just_pressed = 2;
|
||||
break;
|
||||
case KeypadKey::k3:
|
||||
just_pressed = 3;
|
||||
break;
|
||||
case KeypadKey::k4:
|
||||
just_pressed = 4;
|
||||
break;
|
||||
case KeypadKey::k5:
|
||||
just_pressed = 5;
|
||||
break;
|
||||
case KeypadKey::k6:
|
||||
just_pressed = 6;
|
||||
break;
|
||||
case KeypadKey::k7:
|
||||
just_pressed = 7;
|
||||
break;
|
||||
case KeypadKey::k8:
|
||||
just_pressed = 8;
|
||||
break;
|
||||
case KeypadKey::k9:
|
||||
just_pressed = 9;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (just_pressed != -1) {
|
||||
digits[cursor_pos] = just_pressed;
|
||||
cursor_pos = MIN(4, cursor_pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
_update_display(digits, cursor_pos);
|
||||
}
|
||||
|
||||
while (get_button_pressed(&button)) {
|
||||
if (button == ButtonKey::b1) {
|
||||
cursor_pos = MAX(0, cursor_pos-1);
|
||||
} else if (button == ButtonKey::b2) {
|
||||
cursor_pos = MIN(4, cursor_pos+1);
|
||||
}
|
||||
_update_display(digits, cursor_pos);
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void print_4bin_rev(char* out_str, uint8_t n) {
|
||||
out_str[0] = ((n & 0b0001) ? '1' : '0');
|
||||
out_str[1] = ((n & 0b0010) ? '1' : '0');
|
||||
out_str[2] = ((n & 0b0100) ? '1' : '0');
|
||||
out_str[3] = ((n & 0b1000) ? '1' : '0');
|
||||
out_str[4] = '\0';
|
||||
}
|
||||
|
||||
static void debug_switches() {
|
||||
clean_bomb();
|
||||
uint8_t switch_state = 0xFF;
|
||||
uint8_t switch_touch_state = 0xFF;
|
||||
uint8_t button_state = 0xFF;
|
||||
|
||||
char bin_buf[5] = {0};
|
||||
char buf[21] = {0};
|
||||
|
||||
KeypadKey key;
|
||||
ButtonKey button;
|
||||
SwitchKey switch_;
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
sprintf(buf, "Button Pressed: %d ", button);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
if (get_button_released(&button)) {
|
||||
sprintf(buf, "Button Released: %d", button);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
if (get_switch_flipped_down(&switch_)) {
|
||||
sprintf(buf, "Switch Down: %d ", switch_);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
if (get_switch_flipped_up(&switch_)) {
|
||||
sprintf(buf, "Switch Up: %d ", switch_);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
if (get_switch_touch_pressed(&switch_)) {
|
||||
sprintf(buf, "Switch Touch: %d ", switch_);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
if (get_switch_touch_released(&switch_)) {
|
||||
sprintf(buf, "Switch Un-touch: %d", switch_);
|
||||
lcd_print(3, 0, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
|
||||
uint8_t new_switch_touch_state = get_switch_touch_state();
|
||||
if (new_switch_touch_state != switch_touch_state) {
|
||||
switch_touch_state = new_switch_touch_state;
|
||||
print_4bin_rev(bin_buf, switch_touch_state);
|
||||
sprintf(buf, "t: %s", bin_buf);
|
||||
lcd_print(0, 1, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
|
||||
uint8_t new_switch_state = get_switch_state();
|
||||
if (new_switch_state != switch_state) {
|
||||
switch_state = new_switch_state;
|
||||
print_4bin_rev(bin_buf, switch_state);
|
||||
sprintf(buf, "s: %s", bin_buf);
|
||||
lcd_print(1, 1, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
|
||||
uint8_t new_button_state = get_button_state();
|
||||
if (new_button_state != button_state) {
|
||||
button_state = new_button_state;
|
||||
print_4bin_rev(bin_buf, button_state);
|
||||
sprintf(buf, "b: %s", bin_buf);
|
||||
lcd_print(2, 1, buf);
|
||||
ESP_LOGI(TAG, "%s", buf);
|
||||
}
|
||||
|
||||
|
||||
if (get_keypad_pressed(&key) && key == KeypadKey::pound) {
|
||||
return;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
static void flashbang() {
|
||||
play_clip_wav(MOUNT_POINT "/flash.wav", true, false, 1, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(4000));
|
||||
|
||||
for (int bright = 255; bright >= 0; bright -= 2) {
|
||||
for (int led_idx = 0; led_idx < IndicatorLED::LED_MAX; led_idx++) {
|
||||
led_set(led_idx, bright, bright, bright);
|
||||
}
|
||||
leds_flush();
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
leds_clear();
|
||||
leds_flush();
|
||||
}
|
||||
|
||||
17
main/steps/step0.h
Normal file
17
main/steps/step0.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef STEP_0_H
|
||||
#define STEP_0_H
|
||||
|
||||
#include "../drivers/all.h"
|
||||
|
||||
#include "setup_wires.h"
|
||||
#include "step1.h"
|
||||
#include "step2.h"
|
||||
#include "step3.h"
|
||||
#include "step4.h"
|
||||
#include "step5.h"
|
||||
#include "step6.h"
|
||||
|
||||
/// Wait for "*9819"
|
||||
void step0(void);
|
||||
|
||||
#endif /* STEP_0_H */
|
||||
290
main/steps/step1.cpp
Normal file
290
main/steps/step1.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
#include "step1.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step1";
|
||||
|
||||
static const char* COLOR_NAMES[] = {
|
||||
"green",
|
||||
"red",
|
||||
"yellow",
|
||||
"blue"
|
||||
};
|
||||
|
||||
static const char* NUM_NAMES[] = {
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four"
|
||||
};
|
||||
|
||||
static uint32_t NEOPIXEL_COLORS[4] = {
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
};
|
||||
|
||||
static std::random_device my_rd;
|
||||
static std::mt19937 my_gen(my_rd());
|
||||
static std::uniform_int_distribution<> zero_to_one(0, 1);
|
||||
static std::uniform_int_distribution<> zero_to_two(0, 2);
|
||||
static std::uniform_int_distribution<> zero_to_three(0, 3);
|
||||
|
||||
static lv_obj_t *old_scr;
|
||||
static lv_obj_t *scr;
|
||||
static lv_obj_t *text = NULL;
|
||||
|
||||
static lv_style_t style_screen;
|
||||
static lv_style_t green_text;
|
||||
static lv_style_t red_text;
|
||||
static lv_style_t yellow_text;
|
||||
static lv_style_t blue_text;
|
||||
|
||||
static lv_style_t* last_style = nullptr;
|
||||
|
||||
static int switch_leds[] = {0, 0, 0, 0};
|
||||
|
||||
static lv_style_t* color_styles[] = {
|
||||
&green_text,
|
||||
&red_text,
|
||||
&yellow_text,
|
||||
&blue_text
|
||||
};
|
||||
|
||||
/// 0 => part a
|
||||
/// 1 => part b
|
||||
/// 2 => part c
|
||||
static int part = 0;
|
||||
|
||||
|
||||
static void init_step(void) {
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
scr = lv_obj_create(NULL);
|
||||
|
||||
// Set the background color of the display to black.
|
||||
lv_style_init(&style_screen);
|
||||
lv_style_set_bg_color(&style_screen, lv_color_black());
|
||||
lv_style_set_text_font(&style_screen, &lv_font_montserrat_32);
|
||||
lv_obj_add_style(scr, &style_screen, LV_STATE_DEFAULT);
|
||||
|
||||
// rgb565
|
||||
lv_color_t green = { .full = 0x0560 };
|
||||
lv_color_t red = { .full = 0xf800 };
|
||||
lv_color_t yellow = { .full = 0xfce0 };
|
||||
lv_color_t blue = { .full = 0x045f };
|
||||
|
||||
lv_style_init(&green_text);
|
||||
lv_style_set_text_color(&green_text, green);
|
||||
|
||||
lv_style_init(&red_text);
|
||||
lv_style_set_text_color(&red_text, red);
|
||||
|
||||
lv_style_init(&yellow_text);
|
||||
lv_style_set_text_color(&yellow_text, yellow);
|
||||
|
||||
lv_style_init(&blue_text);
|
||||
lv_style_set_text_color(&blue_text, blue);
|
||||
|
||||
text = lv_label_create(scr);
|
||||
|
||||
old_scr = lv_scr_act();
|
||||
lv_scr_load(scr);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void clean_up_step(void) {
|
||||
while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
lv_scr_load(old_scr);
|
||||
lv_obj_del(scr);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
|
||||
static void generate_switch_leds(void) {
|
||||
int colors[4] = {0, 1, 2, 3};
|
||||
|
||||
int idx = zero_to_three(my_gen);
|
||||
switch_leds[0] = colors[idx];
|
||||
colors[idx] = colors[3];
|
||||
|
||||
idx = zero_to_two(my_gen);
|
||||
switch_leds[1] = colors[idx];
|
||||
colors[idx] = colors[2];
|
||||
|
||||
idx = zero_to_one(my_gen);
|
||||
switch_leds[2] = colors[idx];
|
||||
colors[idx] = colors[1];
|
||||
|
||||
switch_leds[3] = colors[0];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
led_set(IndicatorLED::LED_S1 - i, NEOPIXEL_COLORS[switch_leds[i]]);
|
||||
}
|
||||
leds_flush();
|
||||
}
|
||||
|
||||
static void set_text_and_color(const char* text_str, int color) {
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_label_set_text(text, text_str);
|
||||
lv_obj_center(text);
|
||||
if (last_style != nullptr) {
|
||||
lv_obj_remove_style(text, last_style, LV_STATE_DEFAULT);
|
||||
}
|
||||
lv_obj_add_style(text, color_styles[color], LV_STATE_DEFAULT);
|
||||
last_style = color_styles[color];
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static int generate_part_a(void) {
|
||||
int text_color = zero_to_three(my_gen);
|
||||
int text_idx = zero_to_three(my_gen);
|
||||
|
||||
const char* text_string = COLOR_NAMES[text_idx];
|
||||
|
||||
set_text_and_color(text_string, text_color);
|
||||
generate_switch_leds();
|
||||
|
||||
// the correct answer is the text color
|
||||
return text_color;
|
||||
}
|
||||
|
||||
static int generate_part_b(void) {
|
||||
int is_color = zero_to_one(my_gen) == 0;
|
||||
if (is_color) {
|
||||
return generate_part_a();
|
||||
}
|
||||
|
||||
int text_color = zero_to_three(my_gen);
|
||||
int text_number = zero_to_three(my_gen);
|
||||
|
||||
const char* text_string = NUM_NAMES[text_number];
|
||||
|
||||
set_text_and_color(text_string, text_color);
|
||||
generate_switch_leds();
|
||||
|
||||
// the correct answer is the number
|
||||
return text_number;
|
||||
}
|
||||
|
||||
static int generate_part_c(void) {
|
||||
int type = zero_to_two(my_gen);
|
||||
if (type != 0) {
|
||||
return generate_part_b();
|
||||
}
|
||||
|
||||
int text_color = zero_to_three(my_gen);
|
||||
const char* text_string = "switch";
|
||||
|
||||
set_text_and_color(text_string, text_color);
|
||||
generate_switch_leds();
|
||||
|
||||
// the correct answer is the switch
|
||||
return text_color + 4;
|
||||
}
|
||||
|
||||
static int generate_part(void) {
|
||||
switch (part) {
|
||||
case 0:
|
||||
return generate_part_a();
|
||||
case 1:
|
||||
return generate_part_b();
|
||||
case 2:
|
||||
return generate_part_c();
|
||||
}
|
||||
ESP_LOGW(TAG, "`part` (%d) was not to in range 0..=2!", part);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void update_lcd_count(int times) {
|
||||
char buf[16] = {0};
|
||||
sprintf(buf, "%d/15", times);
|
||||
lcd_print(1, 14, buf);
|
||||
}
|
||||
|
||||
static bool play_part(uint32_t time) {
|
||||
set_module_time(time);
|
||||
|
||||
lcd_clear();
|
||||
switch (part) {
|
||||
case 0:
|
||||
lcd_print(1, 1, "COLOR");
|
||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_PINK);
|
||||
break;
|
||||
case 1:
|
||||
lcd_print(1, 1, "NUMBER");
|
||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_BLUE);
|
||||
break;
|
||||
case 2:
|
||||
lcd_print(1, 1, "SWITCH");
|
||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_YELLOW);
|
||||
break;
|
||||
}
|
||||
leds_flush();
|
||||
|
||||
int times = 0;
|
||||
int correct = generate_part();
|
||||
update_lcd_count(times);
|
||||
|
||||
ButtonKey button;
|
||||
SwitchKey switch_;
|
||||
|
||||
while (times < 15) {
|
||||
while (get_button_pressed(&button)) {
|
||||
start_module_timer();
|
||||
if ((int)(button) == correct) {
|
||||
times++;
|
||||
if (times >= 15) break;
|
||||
correct = generate_part();
|
||||
update_lcd_count(times);
|
||||
} else {
|
||||
strike("Incorrect action");
|
||||
}
|
||||
}
|
||||
while (get_switch_flipped(&switch_)) {
|
||||
start_module_timer();
|
||||
if (switch_leds[(int)(switch_)] + 4 == correct) {
|
||||
times++;
|
||||
if (times >= 15) break;
|
||||
correct = generate_part();
|
||||
update_lcd_count(times);
|
||||
} else {
|
||||
strike("Incorrect action");
|
||||
}
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
strike("Out of time");
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
return false;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
stop_module_timer();
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_label_set_text(text, "");
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
if (part < 2) {
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
} else {
|
||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void step1(void) {
|
||||
while (get_switch_flipped(nullptr));
|
||||
|
||||
init_step();
|
||||
while (!play_part(40*1000));
|
||||
part = 1;
|
||||
while (!play_part(35*1000));
|
||||
part = 2;
|
||||
while (!play_part(30*1000));
|
||||
|
||||
clean_up_step();
|
||||
// TODO: flash lights
|
||||
}
|
||||
10
main/steps/step1.h
Normal file
10
main/steps/step1.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef STEP_1_H
|
||||
#define STEP_1_H
|
||||
|
||||
#include <random>
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
|
||||
void step1(void);
|
||||
|
||||
#endif /* STEP_1_H */
|
||||
129
main/steps/step2.cpp
Normal file
129
main/steps/step2.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "step2.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step2";
|
||||
|
||||
static const int NUM_SOLVES = 4;
|
||||
|
||||
// one: 0b00000110
|
||||
// seven: 0b00000111
|
||||
static const uint8_t SSEG_NUMS[8] = {0b00111111, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b01111111, 0b01101111};
|
||||
static const uint8_t SSEG_MAPS[5][4] = {
|
||||
{0b0101101, 0b1111010, 0b1000010, 0b1010100},
|
||||
{0b01000101, 0b00100100, 0b00110110, 0b01111011},
|
||||
{0b00101010, 0b00000010, 0b00010111, 0b00111100},
|
||||
{0b00111000, 0b01010010, 0b00101011, 0b00111010},
|
||||
{0b01000111, 0b00011001, 0b01111000, 0b00111110}
|
||||
};
|
||||
|
||||
static const uint32_t INDICATOR_COLORS[5] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_WHITE,
|
||||
};
|
||||
|
||||
// random number generators
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
static std::uniform_int_distribution<> answer_dist(0, 7);
|
||||
static std::uniform_int_distribution<> map_dist(0, 4);
|
||||
static std::uniform_int_distribution<> display_dist(0, 3);
|
||||
static std::uniform_int_distribution<> random_segment_dist(0, 127);
|
||||
|
||||
static int answer = 0;
|
||||
static uint8_t answer_sseg = SSEG_NUMS[0];
|
||||
static char answer_char = '0';
|
||||
static uint8_t display_map[4] = {0b00000000, 0b00000000, 0b00000000, 0b00000000};
|
||||
static int chosen_map = 0;
|
||||
|
||||
std::map<int, int> number_map = {
|
||||
{0, 0},
|
||||
{1, 2},
|
||||
{2, 3},
|
||||
{3, 4},
|
||||
{4, 5},
|
||||
{5, 6},
|
||||
{6, 8},
|
||||
{7, 9},
|
||||
};
|
||||
|
||||
static void new_puzzle(void) {
|
||||
// scramble lights
|
||||
for (int i = 0; i < 5; i++) {
|
||||
led_set(IndicatorLED::LED_SPEAKER, INDICATOR_COLORS[map_dist(gen)]);
|
||||
leds_flush();
|
||||
|
||||
uint8_t random_segments[4] = {0, 0, 0, 0};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
random_segments[i] = random_segment_dist(gen);
|
||||
}
|
||||
set_module_sseg_raw(random_segments);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
answer = answer_dist(gen);
|
||||
answer_sseg = SSEG_NUMS[answer];
|
||||
// convert answer to number value to account for missing 1 and 7
|
||||
answer = number_map[answer];
|
||||
answer_char = '0' + answer;
|
||||
// ESP_LOGI(TAG, "Answer: %i", answer);
|
||||
|
||||
chosen_map = map_dist(gen);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
display_map[i] = SSEG_MAPS[chosen_map][i];
|
||||
}
|
||||
// ESP_LOGI(TAG, "Chosen Map: %i", chosen_map);
|
||||
|
||||
led_set(IndicatorLED::LED_SPEAKER, INDICATOR_COLORS[chosen_map]);
|
||||
leds_flush();
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bool bit = (answer_sseg >> i) & 1;
|
||||
if (bit == 1) {
|
||||
// choose display and flip bit
|
||||
int display = display_dist(gen);
|
||||
display_map[display] ^= (1 << i);
|
||||
// ESP_LOGI(TAG, "Flipping bit %i on display %i", i, display);
|
||||
}
|
||||
}
|
||||
set_module_sseg_raw(display_map);
|
||||
}
|
||||
|
||||
void step2(void) {
|
||||
KeypadKey key;
|
||||
int solved_times = 0;
|
||||
|
||||
new_puzzle();
|
||||
int strike_time = 0;
|
||||
while(solved_times < NUM_SOLVES) {
|
||||
// for every bit in the answer-
|
||||
if (get_keypad_pressed(&key)) {
|
||||
lcd_clear();
|
||||
char c = char_of_keypad_key(key);
|
||||
// ESP_LOGI(TAG, "Pressed: %c", c);
|
||||
if (c == answer_char) {
|
||||
solved_times++;
|
||||
if (solved_times < NUM_SOLVES) {
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
clean_bomb();
|
||||
new_puzzle();
|
||||
} else {
|
||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||
clear_module_sseg();
|
||||
}
|
||||
} else {
|
||||
strike_time = xTaskGetTickCount();
|
||||
strike("Incorrect Character!");
|
||||
}
|
||||
}
|
||||
|
||||
if (strike_time != 0 && xTaskGetTickCount() - strike_time > pdMS_TO_TICKS(5000)) {
|
||||
strike_time = 0;
|
||||
lcd_clear();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
}
|
||||
12
main/steps/step2.h
Normal file
12
main/steps/step2.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef STEP_2_H
|
||||
#define STEP_2_H
|
||||
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <map>
|
||||
|
||||
void step2(void);
|
||||
|
||||
#endif /* STEP_2_H */
|
||||
422
main/steps/step3.cpp
Normal file
422
main/steps/step3.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
#include "step3.h"
|
||||
|
||||
#define ONE_SECOND_TIME 90'000
|
||||
#define THREE_SECOND_TIME 90'000
|
||||
#define SIX_SECOND_TIME 75'000
|
||||
|
||||
#define TIMES_TO_COMPLETE 4
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step3";
|
||||
|
||||
static int tone = 0;
|
||||
static int times = 0;
|
||||
|
||||
static const char* TONE_FILES[] = {
|
||||
MOUNT_POINT "/low-1.wav",
|
||||
MOUNT_POINT "/low-3.wav",
|
||||
MOUNT_POINT "/low-6.wav",
|
||||
MOUNT_POINT "/high-1.wav",
|
||||
MOUNT_POINT "/high-3.wav",
|
||||
MOUNT_POINT "/high-6.wav",
|
||||
};
|
||||
|
||||
static const size_t LCD_STRING_SOMETHING = 0;
|
||||
static const size_t LCD_STRING_NOTHING = 1;
|
||||
static const char* LCD_STRINGS[] = {
|
||||
"something",
|
||||
"nothing",
|
||||
"",
|
||||
"a word",
|
||||
"somethink",
|
||||
"what?",
|
||||
"LCD",
|
||||
"display",
|
||||
};
|
||||
|
||||
static int indicator_led_idxs[LED_COUNT] = {0};
|
||||
|
||||
static bool contains_coconut = false;
|
||||
static const char* COCONUT = "coconut";
|
||||
static char lcd_random_char_set[] = "aeiou tnsrhldm";
|
||||
static char random_lcd_text[21] = {0};
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
static std::uniform_int_distribution<> tone_dist(0, 5);
|
||||
static std::uniform_int_distribution<> color_dist(0, 6);
|
||||
static std::uniform_int_distribution<> lcd_string_dist(0, 7);
|
||||
static std::uniform_int_distribution<> lcd_number_dist(0, 15);
|
||||
static std::uniform_int_distribution<> lcd_rand_char_dist(0, sizeof(lcd_random_char_set)-2);
|
||||
static std::uniform_int_distribution<> has_coconut_dist(0, 2);
|
||||
static std::uniform_int_distribution<> coconut_position_dist(0, 13);
|
||||
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_RED = 0;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_YELLOW = 1;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_GREEN = 2;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_BLUE = 3;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_PINK = 4;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_WHITE = 5;
|
||||
const static uint32_t NEOPIXEL_COLOR_IDX_OFF = 6;
|
||||
static uint32_t NEOPIXEL_COLORS[7] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_PINK,
|
||||
LEDColor::LED_COLOR_WHITE,
|
||||
LEDColor::LED_COLOR_OFF,
|
||||
};
|
||||
|
||||
static bool one_second();
|
||||
static bool three_second();
|
||||
static bool six_second();
|
||||
|
||||
void step3(void) {
|
||||
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||
if (continue_sem == nullptr) {
|
||||
ESP_LOGE(TAG, "could not create semaphore");
|
||||
return;
|
||||
}
|
||||
|
||||
StarCodeEntry start_code = {
|
||||
.code = "1642",
|
||||
.display_text = "Starting...",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = nullptr,
|
||||
.triggered_sem = continue_sem,
|
||||
};
|
||||
|
||||
add_star_code(start_code);
|
||||
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||
rm_star_code(start_code.code);
|
||||
vSemaphoreDelete(continue_sem);
|
||||
|
||||
while (times < TIMES_TO_COMPLETE) {
|
||||
tone = tone_dist(gen);
|
||||
// tone = 2;
|
||||
while (get_button_pressed(nullptr)) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
play_clip_wav(MOUNT_POINT "/ready.wav", true, false, 3, 0);
|
||||
// The high pitched tones need to be scaled down by 3 more
|
||||
play_clip_wav(TONE_FILES[tone], false, false, 1 + (tone/3) * 4, 0);
|
||||
|
||||
bool correct = false;
|
||||
switch (tone % 3) {
|
||||
case 0:
|
||||
correct = one_second();
|
||||
break;
|
||||
case 1:
|
||||
correct = three_second();
|
||||
break;
|
||||
case 2:
|
||||
correct = six_second();
|
||||
break;
|
||||
}
|
||||
if (correct) {
|
||||
times++;
|
||||
clean_bomb();
|
||||
if (times < TIMES_TO_COMPLETE) {
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
} else {
|
||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||
}
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(1500));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_random_lcd_text(void) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int char_idx = lcd_rand_char_dist(gen);
|
||||
random_lcd_text[i] = lcd_random_char_set[char_idx];
|
||||
}
|
||||
contains_coconut = (has_coconut_dist(gen) == 0);
|
||||
if (contains_coconut) {
|
||||
int idx = coconut_position_dist(gen);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
random_lcd_text[idx+i] = COCONUT[i];
|
||||
// ESP_LOGI(TAG, "Writing idx %d to %c. Is %c", idx+i, COCONUT[i], random_lcd_text[idx+i]);
|
||||
}
|
||||
// ESP_LOGI(TAG, "Now: %s", random_lcd_text);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the leds to random values.
|
||||
///
|
||||
/// This does not flush the leds.
|
||||
static void rng_leds() {
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
indicator_led_idxs[i] = color_dist(gen);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_leds() {
|
||||
// update all the leds
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
led_set(i, NEOPIXEL_COLORS[indicator_led_idxs[i]]);
|
||||
}
|
||||
leds_flush();
|
||||
}
|
||||
|
||||
static uint8_t four_bit_flag(bool b0, bool b1, bool b2, bool b3) {
|
||||
return
|
||||
(b0 << 0) |
|
||||
(b1 << 1) |
|
||||
(b2 << 2) |
|
||||
(b3 << 3)
|
||||
;
|
||||
}
|
||||
|
||||
static void print_4bin(char* out_str, uint8_t n) {
|
||||
out_str[0] = ((n & 0b1000) ? '1' : '0');
|
||||
out_str[1] = ((n & 0b0100) ? '1' : '0');
|
||||
out_str[2] = ((n & 0b0010) ? '1' : '0');
|
||||
out_str[3] = ((n & 0b0001) ? '1' : '0');
|
||||
out_str[4] = ' ';
|
||||
out_str[5] = 'i';
|
||||
out_str[6] = 'n';
|
||||
out_str[7] = ' ';
|
||||
out_str[8] = 'o';
|
||||
out_str[9] = 'r';
|
||||
out_str[10] = 'd';
|
||||
out_str[11] = 'e';
|
||||
out_str[12] = 'r';
|
||||
out_str[13] = ':';
|
||||
out_str[14] = ' ';
|
||||
out_str[15] = ((n & 0b0001) ? '1' : '0');
|
||||
out_str[16] = ((n & 0b0010) ? '1' : '0');
|
||||
out_str[17] = ((n & 0b0100) ? '1' : '0');
|
||||
out_str[18] = ((n & 0b1000) ? '1' : '0');
|
||||
}
|
||||
|
||||
static void debug_correct_values(uint8_t correct_buttons, uint8_t button_mask, uint8_t correct_switches) {
|
||||
char buf[20] = {0};
|
||||
print_4bin(buf, correct_switches);
|
||||
ESP_LOGI(TAG, "Expected Switch State: 0b%s", buf);
|
||||
print_4bin(buf, correct_buttons);
|
||||
ESP_LOGI(TAG, "Expected Button State: 0b%s", buf);
|
||||
print_4bin(buf, button_mask);
|
||||
ESP_LOGI(TAG, "Button Mask: 0b%s", buf);
|
||||
}
|
||||
|
||||
static void debug_actual_values(uint8_t buttons, uint8_t switch_) {
|
||||
char buf[20] = {0};
|
||||
print_4bin(buf, switch_);
|
||||
ESP_LOGI(TAG, "Actual Switch State: 0b%s", buf);
|
||||
print_4bin(buf, buttons);
|
||||
ESP_LOGI(TAG, "Actual Button State: 0b%s", buf);
|
||||
ESP_LOGI(TAG, "");
|
||||
}
|
||||
|
||||
static void wait_for_timer(void) {
|
||||
KeypadKey key;
|
||||
while (get_module_time() > 0) {
|
||||
if (get_keypad_pressed(&key) && key == KeypadKey::kd) {
|
||||
set_module_time(0);
|
||||
return;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
static bool one_second() {
|
||||
clean_bomb();
|
||||
set_module_time(ONE_SECOND_TIME);
|
||||
start_module_timer();
|
||||
|
||||
rng_leds();
|
||||
int speaker_color = indicator_led_idxs[IndicatorLED::LED_SPEAKER];
|
||||
int lcd_string_idx = lcd_string_dist(gen);
|
||||
bool was_high = (tone / 3) == 1;
|
||||
write_leds();
|
||||
lcd_clear();
|
||||
lcd_print(1, 1, LCD_STRINGS[lcd_string_idx]);
|
||||
|
||||
int red_led_count = 0;
|
||||
int blue_led_count = 0;
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_RED) {
|
||||
red_led_count++;
|
||||
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||
blue_led_count++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t correct_switches = four_bit_flag(
|
||||
speaker_color == NEOPIXEL_COLOR_IDX_RED || speaker_color == NEOPIXEL_COLOR_IDX_YELLOW || speaker_color == NEOPIXEL_COLOR_IDX_PINK,
|
||||
lcd_string_idx == LCD_STRING_SOMETHING || lcd_string_idx == LCD_STRING_NOTHING,
|
||||
was_high,
|
||||
!was_high
|
||||
);
|
||||
|
||||
uint8_t correct_button_mask = 0b1011;
|
||||
uint8_t correct_buttons = four_bit_flag(
|
||||
indicator_led_idxs[IndicatorLED::LED_LCD] != 6, // green
|
||||
red_led_count > blue_led_count, // red
|
||||
0, // yellow UNCHECKED
|
||||
indicator_led_idxs[IndicatorLED::LED_RFID] == 4 || indicator_led_idxs[IndicatorLED::LED_RFID] == 6 // blue
|
||||
);
|
||||
|
||||
debug_correct_values(correct_buttons, correct_button_mask, correct_switches);
|
||||
|
||||
wait_for_timer();
|
||||
|
||||
debug_actual_values(get_button_state(), get_switch_state());
|
||||
|
||||
if (get_switch_state() != correct_switches) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Switches");
|
||||
return false;
|
||||
}
|
||||
if ((get_button_state() & correct_button_mask) != correct_buttons) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Buttons");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool three_second() {
|
||||
clean_bomb();
|
||||
set_module_time(THREE_SECOND_TIME);
|
||||
start_module_timer();
|
||||
|
||||
int lcd_number = lcd_number_dist(gen);
|
||||
char lcd_number_string[9] = {0};
|
||||
sprintf(lcd_number_string, "%d", lcd_number);
|
||||
lcd_print(1, 1, lcd_number_string);
|
||||
|
||||
bool was_high = (tone / 3) == 1;
|
||||
|
||||
rng_leds();
|
||||
write_leds();
|
||||
int red_led_count = 0;
|
||||
int blue_led_count = 0;
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_RED) {
|
||||
red_led_count++;
|
||||
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||
blue_led_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// reverse the ordering of the bits
|
||||
uint8_t correct_switches = four_bit_flag(
|
||||
(lcd_number >> 3) & 1,
|
||||
(lcd_number >> 2) & 1,
|
||||
(lcd_number >> 1) & 1,
|
||||
(lcd_number >> 0) & 1
|
||||
);
|
||||
if (!was_high) {
|
||||
correct_switches = (~correct_switches) & 0b1111;
|
||||
}
|
||||
|
||||
uint8_t correct_button_mask = 0b1110;
|
||||
uint8_t correct_buttons = four_bit_flag(
|
||||
0, // green UNCHECKED
|
||||
was_high, // red
|
||||
(lcd_number % 2) == 0, // yellow
|
||||
blue_led_count > red_led_count // blue
|
||||
);
|
||||
|
||||
debug_correct_values(correct_buttons, correct_button_mask, correct_switches);
|
||||
|
||||
wait_for_timer();
|
||||
|
||||
debug_actual_values(get_button_state(), get_switch_state());
|
||||
|
||||
if (get_switch_state() != correct_switches) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Switches");
|
||||
return false;
|
||||
}
|
||||
if ((get_button_state() & correct_button_mask) != correct_buttons) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Buttons");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool six_second() {
|
||||
clean_bomb();
|
||||
set_module_time(SIX_SECOND_TIME);
|
||||
start_module_timer();
|
||||
|
||||
generate_random_lcd_text();
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
lcd_print(0, 0, random_lcd_text);
|
||||
|
||||
int vowels = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
char c = random_lcd_text[i];
|
||||
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
|
||||
vowels++;
|
||||
}
|
||||
}
|
||||
|
||||
bool was_high = (tone / 3) == 1;
|
||||
|
||||
bool second_switch_correct_state = (indicator_led_idxs[IndicatorLED::LED_S2] == NEOPIXEL_COLOR_IDX_RED) || (indicator_led_idxs[IndicatorLED::LED_S2] == NEOPIXEL_COLOR_IDX_OFF);
|
||||
second_switch_correct_state = second_switch_correct_state || was_high;
|
||||
|
||||
rng_leds();
|
||||
write_leds();
|
||||
|
||||
int green_led_count = 0;
|
||||
int blue_led_count = 0;
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||
blue_led_count++;
|
||||
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_GREEN) {
|
||||
green_led_count++;
|
||||
}
|
||||
}
|
||||
|
||||
int pink_led_on_bottom_count = 0;
|
||||
for (int i = IndicatorLED::LED_RFID; i < LED_COUNT; i++) {
|
||||
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_PINK) {
|
||||
pink_led_on_bottom_count++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t correct_switches = four_bit_flag(
|
||||
vowels > 7,
|
||||
second_switch_correct_state,
|
||||
true,
|
||||
!(pink_led_on_bottom_count > 1)
|
||||
);
|
||||
|
||||
uint8_t correct_button_mask = 0b1101;
|
||||
uint8_t correct_buttons = four_bit_flag(
|
||||
(!was_high) || (green_led_count >= 2) || indicator_led_idxs[IndicatorLED::LED_KEYPAD] == 4, // green
|
||||
0, // red UNCHECKED
|
||||
blue_led_count >= 3, // yellow
|
||||
contains_coconut // blue
|
||||
);
|
||||
|
||||
debug_correct_values(correct_buttons, correct_button_mask, correct_switches);
|
||||
|
||||
wait_for_timer();
|
||||
|
||||
debug_actual_values(get_button_state(), get_switch_state());
|
||||
|
||||
if (get_switch_state() != correct_switches) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Switches");
|
||||
return false;
|
||||
}
|
||||
if ((get_button_state() & correct_button_mask) != correct_buttons) {
|
||||
clean_bomb();
|
||||
strike("Incorrect Buttons");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
10
main/steps/step3.h
Normal file
10
main/steps/step3.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef STEP_3_H
|
||||
#define STEP_3_H
|
||||
|
||||
#include <random>
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
|
||||
void step3(void);
|
||||
|
||||
#endif /* STEP_3_H */
|
||||
760
main/steps/step4.cpp
Normal file
760
main/steps/step4.cpp
Normal file
@ -0,0 +1,760 @@
|
||||
#include "step4.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step4";
|
||||
|
||||
static lv_obj_t *old_scr;
|
||||
static lv_obj_t* scr;
|
||||
static lv_obj_t* img;
|
||||
|
||||
static const int height = 22;
|
||||
static const int width = 10;
|
||||
|
||||
static int board[height][width] = {0};
|
||||
static lv_obj_t* visual_board[height][width] = {0};
|
||||
|
||||
static lv_obj_t* line_clear_img;
|
||||
static lv_style_t game_over_style;
|
||||
static lv_obj_t* game_over_label;
|
||||
|
||||
#define MUSIC_FILE MOUNT_POINT "/piano.wav"
|
||||
|
||||
#ifdef TETRIS_USE_FLASH_BG_IMG
|
||||
LV_IMG_DECLARE(bg);
|
||||
static const void* BACKGROUND_SRC = (void*) &bg;
|
||||
#else
|
||||
static const void* BACKGROUND_SRC = (void*)"A:" MOUNT_POINT "/bg.bin";
|
||||
#endif
|
||||
|
||||
#ifdef TETRIS_USE_FLASH_IMG
|
||||
LV_IMG_DECLARE(clear);
|
||||
LV_IMG_DECLARE(db);
|
||||
LV_IMG_DECLARE(green);
|
||||
LV_IMG_DECLARE(lb);
|
||||
LV_IMG_DECLARE(orange);
|
||||
LV_IMG_DECLARE(purple);
|
||||
LV_IMG_DECLARE(red);
|
||||
LV_IMG_DECLARE(yellow);
|
||||
|
||||
static const void* LINE_CLEAR_SRC = (void*) &clear;
|
||||
|
||||
static const void* PIECE_IMG_SRC[] = {
|
||||
NULL,
|
||||
&lb,
|
||||
&db,
|
||||
&orange,
|
||||
&yellow,
|
||||
&green,
|
||||
&purple,
|
||||
&red,
|
||||
};
|
||||
#else
|
||||
static const void* LINE_CLEAR_SRC = (void*)"A:" MOUNT_POINT "/clear.bin";
|
||||
|
||||
static const void* PIECE_IMG_SRC[] = {
|
||||
NULL,
|
||||
"A:" MOUNT_POINT "/lb.bin",
|
||||
"A:" MOUNT_POINT "/db.bin",
|
||||
"A:" MOUNT_POINT "/orange.bin",
|
||||
"A:" MOUNT_POINT "/yellow.bin",
|
||||
"A:" MOUNT_POINT "/green.bin",
|
||||
"A:" MOUNT_POINT "/purple.bin",
|
||||
"A:" MOUNT_POINT "/red.bin",
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool game = true;
|
||||
|
||||
static int target_score = 0;
|
||||
static int score = 0;
|
||||
|
||||
static int piece = 0;
|
||||
static int piece_rotation = 0;
|
||||
static int piece_location[] = {0, 0};
|
||||
static int piece_nodes[4][2] = {
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
lv_obj_t* piece_imgs[4] = {};
|
||||
|
||||
static bool music_playing;
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
static std::uniform_int_distribution<> piece_dist(2, 7);
|
||||
|
||||
static void generate_block();
|
||||
static void show_board();
|
||||
static void get_node_locations();
|
||||
static bool check_overlap();
|
||||
|
||||
static void line_clear(int hi);
|
||||
static void check_line_clears();
|
||||
static void place_piece();
|
||||
|
||||
static void move_left();
|
||||
static void move_right();
|
||||
static void drop();
|
||||
static void rotate_block();
|
||||
static void update_score();
|
||||
static void clear_board();
|
||||
|
||||
|
||||
static int bbcc(int i) { // [0,1,2,3] -> [ 0, 0,+1,+1]
|
||||
const int map[] = {0, 0, 1, 1};
|
||||
return map[i];
|
||||
}
|
||||
static int bccb(int i) { // [0,1,2,3] -> [ 0,+1,+1, 0]
|
||||
const int map[] = {0, 1, 1, 0};
|
||||
return map[i];
|
||||
}
|
||||
static int ccbb(int i) { // [0,1,2,3] -> [+1,+1, 0, 0]
|
||||
const int map[] = {1, 1, 0, 0};
|
||||
return map[i];
|
||||
}
|
||||
|
||||
static int acca(int i) { // [0,1,2,3] -> [-1,+1,+1,-1]
|
||||
const int map[] = {-1, 1, 1, -1};
|
||||
return map[i];
|
||||
}
|
||||
static int aacc(int i) { // [0,1,2,3] -> [-1,-1,+1,+1]
|
||||
const int map[] = {-1, -1, 1, 1};
|
||||
return map[i];
|
||||
}
|
||||
static int babc(int i) { // [0,1,2,3] -> [ 0,-1, 0,+1]
|
||||
const int map[] = {0, -1, 0, 1};
|
||||
return map[i];
|
||||
}
|
||||
static int abcb(int i) { // [0,1,2,3] -> [-1, 0,+1, 0]
|
||||
const int map[] = {-1, 0, 1, 0};
|
||||
return map[i];
|
||||
}
|
||||
|
||||
static int acdb(int i) { // [0,1,2,3] -> [-1,+1,+2, 0]
|
||||
const int map[] = {-1, +1, 2, 0};
|
||||
return map[i];
|
||||
}
|
||||
static int bacd(int i) { // [0,1,2,3] -> [ 0,-1,+1,+2]
|
||||
const int map[] = {0, -1, 1, 2};
|
||||
return map[i];
|
||||
}
|
||||
static int dcab(int i) { // [0,1,2,3] -> [+2,+1,-1, 0]
|
||||
const int map[] = {2, 1, -1, 0};
|
||||
return map[i];
|
||||
}
|
||||
static int bdca(int i) { // [0,1,2,3] -> [ 0,+2,+1,-1]
|
||||
const int map[] = {0, 2, 1, -1};
|
||||
return map[i];
|
||||
}
|
||||
|
||||
static void init_screen() {
|
||||
while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
scr = lv_obj_create(NULL);
|
||||
|
||||
img = lv_img_create(scr);
|
||||
lv_img_set_src(img, BACKGROUND_SRC);
|
||||
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
line_clear_img = lv_img_create(scr);
|
||||
lv_obj_add_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_img_set_src(line_clear_img, LINE_CLEAR_SRC);
|
||||
lv_obj_align(line_clear_img, LV_ALIGN_BOTTOM_LEFT, 159, -(height*16));
|
||||
|
||||
lv_style_init(&game_over_style);
|
||||
lv_style_set_text_color(&game_over_style, lv_color_white());
|
||||
lv_style_set_bg_color(&game_over_style, lv_color_black());
|
||||
lv_style_set_bg_opa(&game_over_style, LV_OPA_100);
|
||||
lv_style_set_text_align(&game_over_style, LV_TEXT_ALIGN_CENTER);
|
||||
|
||||
game_over_label = lv_label_create(scr);
|
||||
lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_align(game_over_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_width(game_over_label, 160);
|
||||
lv_obj_add_style(game_over_label, &game_over_style, LV_STATE_DEFAULT);
|
||||
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
visual_board[h][w] = lv_img_create(scr);
|
||||
lv_obj_align(visual_board[h][w], LV_ALIGN_BOTTOM_LEFT, 159 + w*16, -(h*16));
|
||||
lv_obj_add_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
piece_imgs[i] = lv_img_create(scr);
|
||||
lv_obj_align(piece_imgs[i], LV_ALIGN_BOTTOM_LEFT, 159, -320);
|
||||
}
|
||||
|
||||
old_scr = lv_scr_act();
|
||||
lv_scr_load(scr);
|
||||
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
|
||||
play_clip_wav(MUSIC_FILE, true, true, 4, 0);
|
||||
music_playing = true;
|
||||
}
|
||||
|
||||
static void deinit_screen() {
|
||||
while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
lv_scr_load(old_scr);
|
||||
lv_obj_del(scr);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
|
||||
if (!stop_clip(MUSIC_FILE, pdMS_TO_TICKS(1000))) {
|
||||
ESP_LOGW(TAG, "Can't stop, addicted to the shindig.");
|
||||
}
|
||||
music_playing = false;
|
||||
}
|
||||
|
||||
static void reveal_board() {
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
if (PIECE_IMG_SRC[board[h][w]]) {
|
||||
lv_img_set_src(visual_board[h][w], PIECE_IMG_SRC[board[h][w]]);
|
||||
lv_obj_clear_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void hide_board() {
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
lv_obj_add_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
|
||||
lv_img_set_src(visual_board[h][w], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool play_game(int time, int required_score) {
|
||||
game = true;
|
||||
target_score = required_score;
|
||||
score = 0;
|
||||
update_score();
|
||||
set_module_time(time);
|
||||
start_module_timer();
|
||||
|
||||
clear_board();
|
||||
|
||||
generate_block();
|
||||
show_board();
|
||||
|
||||
ButtonKey button;
|
||||
while (get_button_pressed(&button));
|
||||
// SwitchKey switch_;
|
||||
|
||||
const TickType_t first_repeat_time = pdMS_TO_TICKS(700);
|
||||
const TickType_t repeat_time = pdMS_TO_TICKS(100);
|
||||
TickType_t down_held = 0;
|
||||
TickType_t last_repeat = 0;
|
||||
|
||||
while(game) {
|
||||
while (get_button_pressed(&button)) {
|
||||
switch (button) {
|
||||
case ButtonKey::b1:
|
||||
move_left();
|
||||
break;
|
||||
case ButtonKey::b2:
|
||||
move_right();
|
||||
break;
|
||||
case ButtonKey::b3:
|
||||
down_held = xTaskGetTickCount();
|
||||
last_repeat = 0;
|
||||
drop();
|
||||
break;
|
||||
case ButtonKey::b4:
|
||||
rotate_block();
|
||||
break;
|
||||
}
|
||||
|
||||
show_board();
|
||||
|
||||
if (score >= required_score) {
|
||||
stop_module_timer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while (get_button_released(&button)) {
|
||||
if (button == ButtonKey::b3) {
|
||||
down_held = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle music with switch4
|
||||
SwitchKey switch_;
|
||||
while (get_switch_flipped(&switch_)) {
|
||||
if (switch_ == SwitchKey::s4) {
|
||||
if (music_playing) {
|
||||
stop_clip(MUSIC_FILE, 0);
|
||||
music_playing = false;
|
||||
} else {
|
||||
play_clip_wav(MUSIC_FILE, true, true, 4, 0);
|
||||
music_playing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (down_held != 0) {
|
||||
// check repeat
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
if (now - down_held > first_repeat_time) {
|
||||
// repeat!
|
||||
if (now - last_repeat > repeat_time) {
|
||||
last_repeat = now;
|
||||
drop();
|
||||
show_board();
|
||||
|
||||
if (score >= required_score) {
|
||||
stop_module_timer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_module_time() <= 0) {
|
||||
stop_module_timer();
|
||||
strike("Out of time");
|
||||
return false;
|
||||
}
|
||||
// if (get_switch_flipped(&switch_)) {
|
||||
// printf("%d\n", piece);
|
||||
// for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
// int* p = piece_nodes[i];
|
||||
// printf("PieceLocation: %d, %d\n", p[0], p[1]);
|
||||
// }
|
||||
// printf("PieceRotation: %d\n", piece_rotation);
|
||||
// }
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
// game over
|
||||
ESP_LOGI(TAG, "Game Over. Score: %d", score);
|
||||
stop_module_timer();
|
||||
strike("Out of room");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void complete() {
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
reveal_board();
|
||||
lv_label_set_text(game_over_label, "Winner!\nPress any button to continue");
|
||||
lv_obj_clear_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
while (get_button_pressed(nullptr));
|
||||
while (1) {
|
||||
if (get_button_pressed(nullptr)) break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
|
||||
hide_board();
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void fail() {
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_label_set_text(game_over_label, "Game Over\nPress any button to continue");
|
||||
lv_obj_clear_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
reveal_board();
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
while (get_button_pressed(nullptr));
|
||||
while (1) {
|
||||
if (get_button_pressed(nullptr)) break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
|
||||
hide_board();
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
void step4() {
|
||||
// TODO: extract to helper function
|
||||
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||
if (continue_sem == nullptr) {
|
||||
ESP_LOGE(TAG, "could not create semaphore");
|
||||
}
|
||||
|
||||
StarCodeEntry start_code = {
|
||||
.code = "3850",
|
||||
.display_text = "Starting...",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = nullptr,
|
||||
.triggered_sem = continue_sem,
|
||||
};
|
||||
|
||||
add_star_code(start_code);
|
||||
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||
rm_star_code(start_code.code);
|
||||
vSemaphoreDelete(continue_sem);
|
||||
|
||||
init_screen();
|
||||
|
||||
while (!play_game(4*60*1000, 2)) fail();
|
||||
// TODO: create constants for common assets, and put them in a folder.
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
complete();
|
||||
while (!play_game(4*60*1000, 4)) fail();
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
complete();
|
||||
while (!play_game(7*60*1000, 8)) fail();
|
||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||
complete();
|
||||
|
||||
deinit_screen();
|
||||
}
|
||||
|
||||
static void show_board() {
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
if (board[h][w] == 9) {
|
||||
board[h][w] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
board[p[0]][p[1]] = 9;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
lv_obj_t* piece_img = piece_imgs[i];
|
||||
lv_obj_align(piece_img, LV_ALIGN_BOTTOM_LEFT, 159 + p[1]*16, -(p[0]*16));
|
||||
}
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_block() {
|
||||
int new_piece = piece_dist(gen);
|
||||
if (new_piece == piece) {
|
||||
new_piece = 1;
|
||||
}
|
||||
|
||||
piece = new_piece;
|
||||
piece_rotation = 0;
|
||||
|
||||
piece_location[0] = 18;
|
||||
piece_location[1] = 4;
|
||||
|
||||
get_node_locations();
|
||||
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
if (board[p[0]][p[1]] != 0) {
|
||||
game = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lv_obj_t* piece_img = piece_imgs[i];
|
||||
lv_img_set_src(piece_img, PIECE_IMG_SRC[piece]);
|
||||
}
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void rotate_block() {
|
||||
piece_rotation++;
|
||||
if (piece_rotation > 3) {
|
||||
piece_rotation = 0;
|
||||
}
|
||||
get_node_locations();
|
||||
|
||||
// Check overlap without moving
|
||||
if (check_overlap()) {
|
||||
|
||||
// Check overlap after moving up 1
|
||||
piece_location[0]--;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
|
||||
// Check overlap after moving down 1
|
||||
piece_location[0]++;
|
||||
piece_location[0]++;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
|
||||
// Check overlap after moving left 1
|
||||
piece_location[0]--;
|
||||
piece_location[1]--;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
|
||||
// Check overlap after moving right 1
|
||||
piece_location[1]++;
|
||||
piece_location[1]++;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
piece_location[1]--;
|
||||
// This is Original Position
|
||||
|
||||
// If Line Piece, check 2 left, 2 right, 2 up and 2 down
|
||||
|
||||
// Check 2 left
|
||||
if (piece == 1 && piece_rotation == 0) {
|
||||
piece_location[1]-=2;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
piece_location[1]+=2;
|
||||
get_node_locations();
|
||||
}
|
||||
// Check 2 up
|
||||
} else if (piece == 1 && piece_rotation == 1) {
|
||||
piece_location[0]+=2;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
piece_location[0]-=2;
|
||||
get_node_locations();
|
||||
}
|
||||
// Check 2 right
|
||||
} else if (piece == 1 && piece_rotation == 2) {
|
||||
piece_location[1]+=2;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
piece_location[1]-=2;
|
||||
get_node_locations();
|
||||
}
|
||||
// Check 2 down
|
||||
} else if (piece == 1 && piece_rotation == 3) {
|
||||
piece_location[0]-=2;
|
||||
get_node_locations();
|
||||
if (check_overlap()) {
|
||||
piece_location[0]+=2;
|
||||
get_node_locations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if nodes are outside of map or interposed onto placed nodes
|
||||
static bool check_overlap() {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
if (p[0] < 0 || p[1] < 0 || p[1] >= width) {
|
||||
return true;
|
||||
} else if (board[p[0]][p[1]] != 0 && board[p[0]][p[1]] != 9) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void move_left() {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
if (p[1] == 0) {
|
||||
return;
|
||||
} else if (board[p[0]][p[1]-1] != 0 && board[p[0]][p[1]-1] != 9) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
piece_location[1]--;
|
||||
get_node_locations();
|
||||
}
|
||||
|
||||
static void move_right() {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
if (p[1] == width-1) {
|
||||
return;
|
||||
} else if (board[p[0]][p[1]+1] != 0 && board[p[0]][p[1]+1] != 9) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
piece_location[1]++;
|
||||
get_node_locations();
|
||||
}
|
||||
|
||||
static void drop() {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
int* p = piece_nodes[i];
|
||||
if (p[0] == 0) {
|
||||
place_piece();
|
||||
check_line_clears();
|
||||
generate_block();
|
||||
return;
|
||||
} else if (board[p[0]-1][p[1]] != 0 && board[p[0]-1][p[1]] != 9) {
|
||||
place_piece();
|
||||
check_line_clears();
|
||||
generate_block();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
piece_location[0]--;
|
||||
get_node_locations();
|
||||
}
|
||||
|
||||
static void place_piece() {
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
if (board[h][w] == 9) {
|
||||
board[h][w] = piece;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||
lv_obj_align(piece_imgs[i], LV_ALIGN_BOTTOM_LEFT, 159, -(height*16));
|
||||
}
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
ESP_LOGI(TAG, "Placed Piece: %d", piece);
|
||||
}
|
||||
|
||||
static void get_node_locations() {
|
||||
piece_nodes[0][0] = piece_location[0];
|
||||
piece_nodes[0][1] = piece_location[1];
|
||||
if (piece == 1) {
|
||||
piece_nodes[0][0] = piece_location[0]-bacd(piece_rotation);
|
||||
piece_nodes[0][1] = piece_location[1]+acdb(piece_rotation);
|
||||
|
||||
piece_nodes[1][0] = piece_location[0]-bbcc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]+bccb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]-bccb(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]+ccbb(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]-bdca(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]+dcab(piece_rotation);
|
||||
} else if (piece == 2) {
|
||||
piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]+babc(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]-abcb(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]-aacc(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]+acca(piece_rotation);
|
||||
} else if (piece == 3) {
|
||||
piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]+babc(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]-abcb(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]-acca(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]-aacc(piece_rotation);
|
||||
} else if (piece == 4) {
|
||||
piece_nodes[1][0] = piece_location[0]+1;
|
||||
piece_nodes[1][1] = piece_location[1];
|
||||
|
||||
piece_nodes[2][0] = piece_location[0];
|
||||
piece_nodes[2][1] = piece_location[1]+1;
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]+1;
|
||||
piece_nodes[3][1] = piece_location[1]+1;
|
||||
} else if (piece == 5) {
|
||||
piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]-acca(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]-aacc(piece_rotation);
|
||||
} else if (piece == 6) {
|
||||
piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]+babc(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]-abcb(piece_rotation);
|
||||
} else if (piece == 7) {
|
||||
piece_nodes[1][0] = piece_location[0]+babc(piece_rotation);
|
||||
piece_nodes[1][1] = piece_location[1]-abcb(piece_rotation);
|
||||
|
||||
piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
|
||||
piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);
|
||||
|
||||
piece_nodes[3][0] = piece_location[0]-aacc(piece_rotation);
|
||||
piece_nodes[3][1] = piece_location[1]+acca(piece_rotation);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_line_clears() {
|
||||
for (int h = height-2; h >= 0; h--) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
if (board[h][w] != 0 && board[h][w] != 9) {
|
||||
if (w == width-1) {
|
||||
line_clear(h);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_score() {
|
||||
char buff[16] = {};
|
||||
sprintf(buff, "%d/%d", score, target_score);
|
||||
lcd_clear();
|
||||
lcd_print(1, 1, buff);
|
||||
}
|
||||
|
||||
static void line_clear(int hi) {
|
||||
for (int h = hi; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
board[h][w] = board[h+1][w];
|
||||
}
|
||||
}
|
||||
for (int w = 0; w < width; w++) {
|
||||
board[height-1][w] = 0;
|
||||
}
|
||||
score++;
|
||||
update_score();
|
||||
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_obj_align(line_clear_img, LV_ALIGN_BOTTOM_LEFT, 159, -(hi*16));
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_obj_clear_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(300));
|
||||
if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||
lv_obj_add_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
|
||||
xSemaphoreGive(xGuiSemaphore);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(300));
|
||||
}
|
||||
}
|
||||
|
||||
void clear_board() {
|
||||
for (int h = 0; h < height; h++) {
|
||||
for (int w = 0; w < width; w++) {
|
||||
board[h][w] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
main/steps/step4.h
Normal file
14
main/steps/step4.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef STEP_4_H
|
||||
#define STEP_4_H
|
||||
|
||||
#include <random>
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TETRIS_USE_FLASH_IMG
|
||||
#define TETRIS_USE_FLASH_BG_IMG
|
||||
|
||||
void step4(void);
|
||||
|
||||
#endif /* STEP_4_H */
|
||||
917
main/steps/step5.cpp
Normal file
917
main/steps/step5.cpp
Normal file
@ -0,0 +1,917 @@
|
||||
#include "step5.h"
|
||||
|
||||
#define TIME_CLEAR 30'000
|
||||
#define TIME_PLANK 40'000
|
||||
#define TIME_EMPTY 40'000
|
||||
#define TIME_NOTHING 25'000
|
||||
#define TIME_BLINK 35'000
|
||||
#define TIME_UMMM 35'000
|
||||
#define TIME_BLANK 40'000
|
||||
#define TIME_WHAT 60'000
|
||||
#define TIME_PLINK 40'000
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step5";
|
||||
|
||||
static const int TIMES_TO_SOLVE = 9;
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
static std::uniform_int_distribution<> puzzle_dist(0, 7);
|
||||
static std::uniform_int_distribution<> led_picker_dist(0, 20);
|
||||
static std::uniform_int_distribution<> led_color_dist(0, 5);
|
||||
|
||||
void set_unique_leds(std::vector<int>& input_options, const int num, const uint32_t color) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
std::uniform_int_distribution<> led_option_dist(0, input_options.size() - 1);
|
||||
int led = led_option_dist(gen);
|
||||
led_set(input_options[led], color);
|
||||
input_options.erase(input_options.begin() + led);
|
||||
}
|
||||
}
|
||||
|
||||
void set_unique_leds_random_color(std::vector<int>& input_options, const int num, const uint32_t* COLORS, size_t COLORS_len) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
std::uniform_int_distribution<> led_option_dist(0, input_options.size() - 1);
|
||||
int led = led_option_dist(gen);
|
||||
|
||||
std::uniform_int_distribution<> led_color_dist(0, COLORS_len - 1);
|
||||
int color = led_color_dist(gen);
|
||||
led_set(input_options[led], COLORS[color]);
|
||||
input_options.erase(input_options.begin() + led);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> unique_values(std::vector<int>& input_options, int num) {
|
||||
std::vector<int> output_vec;
|
||||
for (int i = 0; i < num; i++) {
|
||||
std::uniform_int_distribution<> option_num_dist(0, input_options.size() - 1);
|
||||
int option_num = option_num_dist(gen);
|
||||
output_vec.push_back(input_options[option_num]);
|
||||
input_options.erase(input_options.begin() + option_num);
|
||||
}
|
||||
return output_vec;
|
||||
}
|
||||
|
||||
bool submit_0(int green_indicators) {
|
||||
uint8_t state = get_switch_state();
|
||||
uint8_t flipped_state = 0;
|
||||
flipped_state |= (state & 0b1) << 3;
|
||||
flipped_state |= (state & 0b10) << 1;
|
||||
flipped_state |= (state & 0b100) >> 1;
|
||||
flipped_state |= (state & 0b1000) >> 3;
|
||||
|
||||
if (flipped_state == green_indicators) {
|
||||
return true;
|
||||
} else {
|
||||
strike("Incorrect Switches");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool submit_1(int num_indicators, std::string pressed_keys, uint8_t starting_switch_state) {
|
||||
if (num_indicators < 18) {
|
||||
if (get_switch_state() == 0b1111) {
|
||||
if (pressed_keys == "") {
|
||||
return true;
|
||||
} else {
|
||||
strike("Incorrect keypad!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
strike("Incorrect switches!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (starting_switch_state == get_switch_state()) {
|
||||
if (pressed_keys == "ADCB") {
|
||||
return true;
|
||||
} else {
|
||||
strike("Incorrect keypad!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
strike("Switches changed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool submit_2(bool* lit_leds, int green_pressed, int blue_pressed, int fingerprint_pressed, std::string keypad_string) {
|
||||
uint8_t switch_state = get_switch_state() & 0b11;
|
||||
|
||||
bool rfid_correct = (lit_leds[0] && (green_pressed == 1)) || (!lit_leds[0] && (green_pressed == 0));
|
||||
ESP_LOGI(TAG, "rfid_correct: %i", rfid_correct);
|
||||
bool lcd_correct = (lit_leds[1] && (blue_pressed == 1)) || (!lit_leds[1] && (blue_pressed == 0));
|
||||
ESP_LOGI(TAG, "lcd_correct: %i", lcd_correct);
|
||||
bool speaker_correct = (lit_leds[2] && (fingerprint_pressed == 2)) || (!lit_leds[2] && (fingerprint_pressed == 0));
|
||||
ESP_LOGI(TAG, "speaker_correct: %i", speaker_correct);
|
||||
bool keypad_correct = (lit_leds[3] && (keypad_string == "12")) || (!lit_leds[3] && (keypad_string == ""));
|
||||
ESP_LOGI(TAG, "keypad_correct: %i", keypad_correct);
|
||||
bool tft_correct = (lit_leds[4] && (switch_state == 0b11)) || !lit_leds[4];
|
||||
ESP_LOGI(TAG, "tft_correct: %i", tft_correct);
|
||||
|
||||
if (rfid_correct && lcd_correct && speaker_correct && keypad_correct && tft_correct) {
|
||||
return true;
|
||||
} else {
|
||||
strike("Incorrect state!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool submit_4(int* button_colors, int* switch_colors, uint8_t initial_switch_state) {
|
||||
if (button_colors[0] == 0 && button_colors[1] == 1 && button_colors[2] == 2 && button_colors[3] == 3) {
|
||||
uint8_t switch_state = get_switch_state();
|
||||
bool correct = true;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (switch_colors[i] == 2) {
|
||||
if ((((switch_state ^ initial_switch_state) >> i) & 0b1) != 1) {
|
||||
correct = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (((switch_state >> i) & 0b1) != (switch_colors[i] & 0b1)) {
|
||||
correct = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (correct) {
|
||||
return true;
|
||||
}
|
||||
strike("Incorrect switches!");
|
||||
return false;
|
||||
} else {
|
||||
strike("Incorrect buttons!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool submit_5(std::array<int, 4> indicator_numbers, std::array<int, 4> buttons_pressed) {
|
||||
if (buttons_pressed != indicator_numbers) {
|
||||
ESP_LOGI(TAG, "Button State: %i, %i, %i, %i, Indicator Numbers: %i, %i, %i, %i", buttons_pressed[0], buttons_pressed[1], buttons_pressed[2], buttons_pressed[3], indicator_numbers[0], indicator_numbers[1], indicator_numbers[2], indicator_numbers[3]);
|
||||
strike("Wrong button state!");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t switch_state = get_switch_state();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ESP_LOGI(TAG, "Switch State: %i, Indicator Number: %i", ((switch_state >> i) & 0b1), indicator_numbers[i]);
|
||||
if (((switch_state >> i) & 0b1) == (indicator_numbers[i] & 0b1)) {
|
||||
strike("Wrong switch state!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool submit_6(bool* buttons_cycling, bool button_turned_on, int led_off) {
|
||||
for (int i = 0; i < sizeof(buttons_cycling); i++) {
|
||||
if ((buttons_cycling[i] == true) || (button_turned_on == false && led_off != -1)) {
|
||||
strike("Incorrect!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void step5(void) {
|
||||
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||
if (continue_sem == nullptr) {
|
||||
ESP_LOGE(TAG, "could not create semaphore");
|
||||
}
|
||||
|
||||
StarCodeEntry start_code = {
|
||||
.code = "2648",
|
||||
.display_text = "Starting...",
|
||||
.delay_us = 2'000'000,
|
||||
.callback = nullptr,
|
||||
.triggered_sem = continue_sem,
|
||||
};
|
||||
|
||||
add_star_code(start_code);
|
||||
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||
rm_star_code(start_code.code);
|
||||
vSemaphoreDelete(continue_sem);
|
||||
|
||||
std::vector<int> all_leds;
|
||||
|
||||
for (int i = 0; i < 21; i++) {
|
||||
all_leds.push_back(i);
|
||||
}
|
||||
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_PINK,
|
||||
LEDColor::LED_COLOR_WHITE,
|
||||
};
|
||||
static std::uniform_int_distribution<> led_number_dist(0, 21);
|
||||
|
||||
int last_cycle_tick = xTaskGetTickCount();
|
||||
int scrambled_times = 0;
|
||||
while (scrambled_times < 5) {
|
||||
if ((xTaskGetTickCount() - last_cycle_tick) > pdMS_TO_TICKS(100)) {
|
||||
clean_bomb();
|
||||
std::vector<int> led_options = all_leds;
|
||||
set_unique_leds_random_color(led_options, led_number_dist(gen), COLORS, sizeof(COLORS)/sizeof(COLORS[0]));
|
||||
leds_flush();
|
||||
last_cycle_tick = xTaskGetTickCount();
|
||||
scrambled_times++;
|
||||
}
|
||||
}
|
||||
|
||||
clean_bomb();
|
||||
int solved_puzzles = 0;
|
||||
while (solved_puzzles < TIMES_TO_SOLVE) {
|
||||
bool solved_correctly = false;
|
||||
|
||||
int puzzle = puzzle_dist(gen);
|
||||
|
||||
switch (puzzle) {
|
||||
case 0: {
|
||||
lcd_print(1, 1, "Clear");
|
||||
set_module_time(TIME_CLEAR);
|
||||
start_module_timer();
|
||||
|
||||
std::vector<int> indicator_options = all_leds;
|
||||
|
||||
// set green
|
||||
std::uniform_int_distribution<> green_indicators_dist(1, 15);
|
||||
uint8_t green_indicators = green_indicators_dist(gen);
|
||||
set_unique_leds(indicator_options, green_indicators, LEDColor::LED_COLOR_GREEN);
|
||||
|
||||
// set non-green
|
||||
const uint32_t NON_GREEN_COLORS[] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_PINK,
|
||||
};
|
||||
std::uniform_int_distribution<> non_green_indicators_dist(0, (20 - green_indicators));
|
||||
set_unique_leds_random_color(indicator_options, non_green_indicators_dist(gen), NON_GREEN_COLORS, sizeof(NON_GREEN_COLORS)/sizeof(NON_GREEN_COLORS[0]));
|
||||
|
||||
leds_flush();
|
||||
|
||||
// wait for submit
|
||||
KeypadKey key;
|
||||
while (1) {
|
||||
if (get_module_time() <= 0 || (get_keypad_pressed(&key) && (char_of_keypad_key(key) == '#'))) {
|
||||
solved_correctly = submit_0(green_indicators);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
lcd_print(1, 1, "Blank");
|
||||
set_module_time(TIME_BLANK);
|
||||
start_module_timer();
|
||||
|
||||
std::uniform_int_distribution<> on_indicators_dist(16, 21);
|
||||
uint8_t indicators_num = on_indicators_dist(gen);
|
||||
|
||||
std::vector<int> indicator_options = all_leds;
|
||||
|
||||
set_unique_leds_random_color(indicator_options, indicators_num, COLORS, sizeof(COLORS)/sizeof(COLORS[0]));
|
||||
leds_flush();
|
||||
|
||||
uint8_t starting_switch_state = get_switch_state();
|
||||
std::string pressed_keys;
|
||||
|
||||
KeypadKey key;
|
||||
while (1) {
|
||||
if (get_keypad_pressed(&key)) {
|
||||
char keypad_char = char_of_keypad_key(key);
|
||||
if (keypad_char == '#') {
|
||||
solved_correctly = submit_1(indicators_num, pressed_keys, starting_switch_state);
|
||||
break;
|
||||
} else {
|
||||
pressed_keys += char_of_keypad_key(key);
|
||||
}
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
solved_correctly = submit_1(indicators_num, pressed_keys, starting_switch_state);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
set_module_time(TIME_EMPTY);
|
||||
start_module_timer();
|
||||
|
||||
std::map<int, int> idx_to_led_map = {
|
||||
{0, 10},
|
||||
{1, 12},
|
||||
{2, 9},
|
||||
{3, 11},
|
||||
{4, 6},
|
||||
};
|
||||
|
||||
std::uniform_int_distribution<> lit_led_dist(0, 1);
|
||||
|
||||
// rfid, lcd, speaker, keypad, tft
|
||||
bool lit_leds[5] = {static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen))};
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (lit_leds[i]) {
|
||||
int color = led_color_dist(gen);
|
||||
led_set(idx_to_led_map[i], COLORS[color]);
|
||||
}
|
||||
}
|
||||
leds_flush();
|
||||
|
||||
int green_button_pressed = 0;
|
||||
int blue_button_pressed = 0;
|
||||
int fingerprint_sensor_pressed = 0;
|
||||
std::string keypad_string;
|
||||
|
||||
KeypadKey key;
|
||||
ButtonKey button;
|
||||
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
uint8_t button_state = get_button_state();
|
||||
|
||||
if ((button_state & 0b1) == 0b1) {
|
||||
green_button_pressed++;
|
||||
if ((green_button_pressed > 1) || !lit_leds[0]) {
|
||||
strike("Too many times!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((button_state & 0b1000) == 0b1000) {
|
||||
blue_button_pressed++;
|
||||
if ((blue_button_pressed > 1) || !lit_leds[1]) {
|
||||
strike("Too many times!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (get_touch_pressed()) {
|
||||
fingerprint_sensor_pressed++;
|
||||
// ESP_LOGI(TAG, "touch sensor pressed");
|
||||
if ((fingerprint_sensor_pressed > 2) || !lit_leds[2]) {
|
||||
strike("Too many times!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (get_keypad_pressed(&key)) {
|
||||
char keypad_char = char_of_keypad_key(key);
|
||||
if (keypad_char == '#') {
|
||||
solved_correctly = submit_2(lit_leds, green_button_pressed, blue_button_pressed, fingerprint_sensor_pressed, keypad_string);
|
||||
break;
|
||||
} else {
|
||||
// ESP_LOGI(TAG, "pressed %c", char_of_keypad_key(key));
|
||||
keypad_string += char_of_keypad_key(key);
|
||||
}
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
solved_correctly = submit_2(lit_leds, green_button_pressed, blue_button_pressed, fingerprint_sensor_pressed, keypad_string);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
lcd_print(1, 1, "Nothing");
|
||||
set_module_time(TIME_NOTHING);
|
||||
start_module_timer();
|
||||
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
};
|
||||
|
||||
static std::uniform_int_distribution<> color_dist(0, 3);
|
||||
|
||||
int tft_color = color_dist(gen);
|
||||
int speaker_color = color_dist(gen);
|
||||
int s3_color = color_dist(gen);
|
||||
|
||||
led_set(IndicatorLED::LED_TFT, COLORS[tft_color]);
|
||||
led_set(IndicatorLED::LED_SPEAKER, COLORS[speaker_color]);
|
||||
led_set(IndicatorLED::LED_S3, COLORS[s3_color]);
|
||||
leds_flush();
|
||||
|
||||
int buttons_pressed = 0;
|
||||
|
||||
ButtonKey button;
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
buttons_pressed++;
|
||||
uint8_t button_state = get_button_state();
|
||||
|
||||
if (buttons_pressed == 1) {
|
||||
if ((button_state >> tft_color) != 0b1) {
|
||||
strike("Wrong button!");
|
||||
break;
|
||||
}
|
||||
} else if (buttons_pressed == 2) {
|
||||
if ((button_state >> speaker_color) != 0b1) {
|
||||
strike("Wrong button!");
|
||||
break;
|
||||
}
|
||||
} else if (buttons_pressed == 3) {
|
||||
if ((button_state >> s3_color) != 0b1) {
|
||||
strike("Wrong button!");
|
||||
} else {
|
||||
solved_correctly = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
strike("Ran out of time!");
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
lcd_print(1, 1, "Blink");
|
||||
set_module_time(TIME_BLINK);
|
||||
start_module_timer();
|
||||
|
||||
// buttons
|
||||
const uint32_t BUTTON_COLORS[] = {
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
};
|
||||
|
||||
static std::uniform_int_distribution<> button_color_dist(0, 3);
|
||||
int button_colors[4] = {button_color_dist(gen), button_color_dist(gen), button_color_dist(gen), button_color_dist(gen)};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
led_set(IndicatorLED::LED_B1 - i, BUTTON_COLORS[button_colors[i]]);
|
||||
}
|
||||
|
||||
// switches
|
||||
const uint32_t SWITCH_COLOR[] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
};
|
||||
|
||||
static std::uniform_int_distribution<> switch_color_dist(0, 2);
|
||||
|
||||
int switch_colors[4] = {switch_color_dist(gen), switch_color_dist(gen), switch_color_dist(gen), switch_color_dist(gen)};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
led_set(IndicatorLED::LED_S1 - i, SWITCH_COLOR[switch_colors[i]]);
|
||||
}
|
||||
|
||||
leds_flush();
|
||||
|
||||
ButtonKey button;
|
||||
KeypadKey key;
|
||||
uint8_t starting_switch_state = get_switch_state();
|
||||
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
uint8_t button_state = get_button_state();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (((button_state >> i) & 0b1) == 0b1) {
|
||||
button_colors[i]++;
|
||||
if (button_colors[i] > 3) {
|
||||
button_colors[i] = 0;
|
||||
}
|
||||
led_set(IndicatorLED::LED_B1 - i, BUTTON_COLORS[button_colors[i]]);
|
||||
}
|
||||
}
|
||||
leds_flush();
|
||||
}
|
||||
if (get_module_time() <= 0 || (get_keypad_pressed(&key) && (char_of_keypad_key(key) == '#'))) {
|
||||
solved_correctly = submit_4(button_colors, switch_colors, starting_switch_state);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
lcd_print(1, 1, "Ummm");
|
||||
set_module_time(TIME_UMMM);
|
||||
start_module_timer();
|
||||
|
||||
std::uniform_int_distribution<> indicator_number_dist(0, 5);
|
||||
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
};
|
||||
|
||||
// green, red, yellow, blue
|
||||
std::array<int, 4> indicator_numbers = {indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen)};
|
||||
std::vector<int> indicator_options = all_leds;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
set_unique_leds(indicator_options, indicator_numbers[i], COLORS[i]);
|
||||
}
|
||||
leds_flush();
|
||||
|
||||
std::array<int, 4> buttons_pressed = {0, 0, 0, 0};
|
||||
ButtonKey button;
|
||||
KeypadKey key;
|
||||
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
uint8_t button_state = get_button_state();
|
||||
bool failed = false;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (((button_state >> i) & 0b1) == 0b1) {
|
||||
buttons_pressed[i]++;
|
||||
if (buttons_pressed[i] > indicator_numbers[i]) {
|
||||
strike("Too many times!");
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (get_module_time() <= 0 || (get_keypad_pressed(&key) && (char_of_keypad_key(key) == '#'))) {
|
||||
ESP_LOGI(TAG, "Keypad Char: %c, time: %lu", char_of_keypad_key(key), get_module_time());
|
||||
solved_correctly = submit_5(indicator_numbers, buttons_pressed);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
lcd_print(1, 1, "Plank");
|
||||
set_module_time(TIME_PLANK);
|
||||
start_module_timer();
|
||||
|
||||
std::uniform_int_distribution<> led_color_dist(0, 5);
|
||||
std::uniform_int_distribution<> led_off_dist(-1, 3);
|
||||
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_PINK,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_WHITE,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
};
|
||||
|
||||
int button_colors[4];
|
||||
bool buttons_cycling[4];
|
||||
int led_off = led_off_dist(gen);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (led_off != i) {
|
||||
button_colors[i] = led_color_dist(gen);
|
||||
led_set(IndicatorLED::LED_B1 - i, COLORS[button_colors[i]]);
|
||||
buttons_cycling[i] = true;
|
||||
} else {
|
||||
button_colors[i] = -1;
|
||||
buttons_cycling[i] = false;
|
||||
}
|
||||
}
|
||||
leds_flush();
|
||||
|
||||
const uint8_t CORRECT_COLORS[4] = {4, 0, 5, 2};
|
||||
TickType_t lastCycleTime = xTaskGetTickCount();
|
||||
bool button_turned_on = false;
|
||||
|
||||
ButtonKey button;
|
||||
KeypadKey key;
|
||||
|
||||
std::uniform_int_distribution<> led_turn_on_dist(0, 3);
|
||||
while (1) {
|
||||
if (get_button_pressed(&button)) {
|
||||
uint8_t button_state = get_button_state();
|
||||
bool failed = false;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (((button_state >> i) & 0b1) == 0b1) {
|
||||
if (button_colors[i] == -1) {
|
||||
if (button_turned_on) {
|
||||
strike("Pressed after on!");
|
||||
failed = true;
|
||||
break;
|
||||
} else if (led_turn_on_dist(gen) == 0) {
|
||||
button_turned_on = true;
|
||||
led_set(IndicatorLED::LED_B1 - i, COLORS[CORRECT_COLORS[i]]);
|
||||
leds_flush();
|
||||
}
|
||||
} else if (button_colors[i] != CORRECT_COLORS[i]) {
|
||||
strike("Wrong time!");
|
||||
failed = true;
|
||||
break;
|
||||
} else {
|
||||
buttons_cycling[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((xTaskGetTickCount() - lastCycleTime) >= pdMS_TO_TICKS(500)) {
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (buttons_cycling[i]) {
|
||||
button_colors[i]++;
|
||||
if (button_colors[i] > 5) {
|
||||
button_colors[i] = 0;
|
||||
}
|
||||
led_set(IndicatorLED::LED_B1 - i, COLORS[button_colors[i]]);
|
||||
}
|
||||
}
|
||||
leds_flush();
|
||||
|
||||
lastCycleTime = xTaskGetTickCount();
|
||||
}
|
||||
if (get_module_time() <= 0 || (get_keypad_pressed(&key) && (char_of_keypad_key(key) == '#'))) {
|
||||
solved_correctly = submit_6(buttons_cycling, button_turned_on, led_off);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
lcd_print(1, 1, "What");
|
||||
set_module_time(TIME_WHAT);
|
||||
start_module_timer();
|
||||
|
||||
std::uniform_int_distribution<> math_number_dist(1, 9);
|
||||
|
||||
std::vector<float> math_numbers;
|
||||
std::vector<int> math_operations;
|
||||
|
||||
std::map<int, char> operation_map = {
|
||||
{0, '+'},
|
||||
{1, '-'},
|
||||
{2, '*'},
|
||||
{3, '/'},
|
||||
};
|
||||
|
||||
int expression_answer = -1;
|
||||
std::string display_expression;
|
||||
|
||||
while (expression_answer < 0) {
|
||||
math_numbers = {static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen))};
|
||||
std::vector<int> possible_math_operations = {0, 1, 2, 3};
|
||||
math_operations = unique_values(possible_math_operations, 3);
|
||||
|
||||
display_expression = std::to_string(static_cast<int>(math_numbers[0]));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
display_expression += operation_map[math_operations[i]];
|
||||
display_expression += std::to_string(static_cast<int>(math_numbers[i + 1]));
|
||||
}
|
||||
|
||||
// Solve
|
||||
for (int j = 0; j < 3; j++) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < math_operations.size(); i++) {
|
||||
if (math_operations[i] == 2) {
|
||||
// ESP_LOGI(TAG, "i = %i, condensing %f * %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] * math_numbers[i+1]));
|
||||
math_numbers[i] = math_numbers[i] * math_numbers[i + 1];
|
||||
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||
math_operations.erase(math_operations.begin() + i);
|
||||
found = true;
|
||||
break;
|
||||
} else if (math_operations[i] == 3) {
|
||||
// ESP_LOGI(TAG, "i = %i, condensing %f / %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] / math_numbers[i+1]));
|
||||
math_numbers[i] = math_numbers[i] / math_numbers[i + 1];
|
||||
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||
math_operations.erase(math_operations.begin() + i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) continue;
|
||||
for (int i = 0; i < math_operations.size(); i++) {
|
||||
if (math_operations[i] == 0) {
|
||||
// ESP_LOGI(TAG, "i = %i, condensing %f + %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] + math_numbers[i+1]));
|
||||
math_numbers[i] = math_numbers[i] + math_numbers[i + 1];
|
||||
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||
math_operations.erase(math_operations.begin() + i);
|
||||
found = true;
|
||||
break;
|
||||
} else if (math_operations[i] == 1) {
|
||||
// ESP_LOGI(TAG, "i = %i, condensing %f - %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] - math_numbers[i+1]));
|
||||
math_numbers[i] = math_numbers[i] - math_numbers[i + 1];
|
||||
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||
math_operations.erase(math_operations.begin() + i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expression_answer = static_cast<int>(std::floor(math_numbers[0]));
|
||||
}
|
||||
|
||||
// display expression
|
||||
lcd_print(2, 1, display_expression.c_str());
|
||||
|
||||
// set LEDs
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
};
|
||||
|
||||
std::uniform_int_distribution<> add_sub_indicator_dist(1, 6);
|
||||
std::uniform_int_distribution<> mult_div_indicator_dist(1, 3);
|
||||
|
||||
int modifier_indicators[4] = {add_sub_indicator_dist(gen), add_sub_indicator_dist(gen), mult_div_indicator_dist(gen), mult_div_indicator_dist(gen)};
|
||||
|
||||
|
||||
while ((((expression_answer + (modifier_indicators[0] * 3) - modifier_indicators[1]) * std::pow(3, modifier_indicators[2])) / std::pow(2, modifier_indicators[3])) < 0) {
|
||||
modifier_indicators[0] = add_sub_indicator_dist(gen);
|
||||
modifier_indicators[1] = add_sub_indicator_dist(gen);
|
||||
modifier_indicators[2] = mult_div_indicator_dist(gen);
|
||||
modifier_indicators[3] = mult_div_indicator_dist(gen);
|
||||
}
|
||||
|
||||
expression_answer += modifier_indicators[0] * 3;
|
||||
expression_answer -= modifier_indicators[1];
|
||||
expression_answer *= std::pow(3, modifier_indicators[2]);
|
||||
expression_answer /= std::pow(2, modifier_indicators[3]);
|
||||
|
||||
std::vector<int> led_options = all_leds;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
set_unique_leds(led_options, modifier_indicators[i], COLORS[i]);
|
||||
}
|
||||
|
||||
leds_flush();
|
||||
|
||||
std::string answer_string = std::to_string(expression_answer);
|
||||
std::string entered_string = "";
|
||||
|
||||
ESP_LOGI(TAG, "Solved full answer: %s", answer_string.c_str());
|
||||
|
||||
KeypadKey key;
|
||||
|
||||
while (1) {
|
||||
if (get_keypad_pressed(&key)) {
|
||||
if (key == KeypadKey::star) {
|
||||
// clear
|
||||
entered_string = "";
|
||||
} else if (key == KeypadKey::pound) {
|
||||
// submit
|
||||
if (entered_string != answer_string) {
|
||||
strike("Incorrect answer!");
|
||||
} else {
|
||||
solved_correctly = true;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
entered_string += char_of_keypad_key(key);
|
||||
}
|
||||
|
||||
lcd_clear();
|
||||
lcd_print(1, 1, "What");
|
||||
lcd_print(2, 1, display_expression.c_str());
|
||||
lcd_print(3, 1, entered_string.c_str());
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
strike("Ran out of time!");
|
||||
break;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
lcd_print(1, 1, "Plink");
|
||||
set_module_time(TIME_PLINK);
|
||||
start_module_timer();
|
||||
|
||||
std::uniform_int_distribution<> indicator_number_dist(0, 4);
|
||||
|
||||
// ESP_LOGI(TAG, "Green: %i, Red: %i, Yellow: %i, Blue: %i", green_indicators, red_indicators, yellow_indicators, blue_indicators);
|
||||
|
||||
const uint32_t COLORS[] = {
|
||||
LEDColor::LED_COLOR_GREEN,
|
||||
LEDColor::LED_COLOR_RED,
|
||||
LEDColor::LED_COLOR_YELLOW,
|
||||
LEDColor::LED_COLOR_BLUE,
|
||||
LEDColor::LED_COLOR_PINK,
|
||||
};
|
||||
|
||||
int solved_times = 0;
|
||||
bool failed = false;
|
||||
while (solved_times < 3 && !failed) {
|
||||
int indicator_numbers[5] = {indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen)};
|
||||
|
||||
std::vector<int> led_options = all_leds;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
set_unique_leds(led_options, indicator_numbers[i], COLORS[i]);
|
||||
}
|
||||
|
||||
leds_flush();
|
||||
|
||||
std::uniform_int_distribution<> answer_color_dist(0, 4);
|
||||
|
||||
std::map<int, std::string> color_name_map = {
|
||||
{0, "Green"},
|
||||
{1, "Red"},
|
||||
{2, "Yellow"},
|
||||
{3, "Blue"},
|
||||
{4, "Pink"},
|
||||
};
|
||||
|
||||
int answer_color = answer_color_dist(gen);
|
||||
|
||||
std::string color_string = color_name_map[answer_color];
|
||||
std::string answer_num = std::to_string(indicator_numbers[answer_color]);
|
||||
|
||||
// ESP_LOGI(TAG, "color string: %s", color_string.c_str());
|
||||
|
||||
lcd_print(2, 1, color_string.c_str());
|
||||
|
||||
std::string entered_string;
|
||||
|
||||
KeypadKey key;
|
||||
while (1) {
|
||||
if (get_keypad_pressed(&key)) {
|
||||
bool failed = false;
|
||||
|
||||
if (key == KeypadKey::star) {
|
||||
// clear
|
||||
entered_string = "";
|
||||
} else if (key == KeypadKey::pound) {
|
||||
// submit
|
||||
if (entered_string != answer_num) {
|
||||
strike("Incorrect answer!");
|
||||
failed = true;
|
||||
} else {
|
||||
solved_correctly = true;;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
entered_string += char_of_keypad_key(key);
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
break;
|
||||
}
|
||||
|
||||
lcd_clear();
|
||||
lcd_print(1, 1, "Plink");
|
||||
lcd_print(2, 1, color_string.c_str());
|
||||
lcd_print(3, 1, entered_string.c_str());
|
||||
}
|
||||
if (get_module_time() <= 0) {
|
||||
strike("Ran out of time!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
if (!failed) {
|
||||
solved_correctly = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
stop_module_timer();
|
||||
if (solved_correctly) {
|
||||
solved_puzzles++;
|
||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
solved_correctly = false;
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
}
|
||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||
clean_bomb();
|
||||
}
|
||||
}
|
||||
16
main/steps/step5.h
Normal file
16
main/steps/step5.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef STEP_5_H
|
||||
#define STEP_5_H
|
||||
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
void step5(void);
|
||||
|
||||
#endif /* STEP_5_H */
|
||||
49
main/steps/step6.cpp
Normal file
49
main/steps/step6.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "step6.h"
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *TAG = "step6";
|
||||
|
||||
static uint8_t cut_wires = 0;
|
||||
|
||||
void step6(void) {
|
||||
get_cut_wires();
|
||||
clear_all_pressed_released();
|
||||
|
||||
WireColor wires[NUM_WIRES];
|
||||
load_wires_from_sd_card(wires);
|
||||
bool solution[NUM_WIRES] = {0};
|
||||
solve_wires(wires, solution);
|
||||
|
||||
while (1) {
|
||||
uint8_t new_cut_wires = get_cut_wires();
|
||||
uint8_t just_cut_wires = new_cut_wires & ~cut_wires;
|
||||
cut_wires |= new_cut_wires;
|
||||
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (just_cut_wires & (1<<i)) {
|
||||
if (solution[i]) {
|
||||
play_clip_wav(MOUNT_POINT "/correct.wav", true, false, 3, 0);
|
||||
} else {
|
||||
strike("Cut wrong wire");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_touch_pressed()) {
|
||||
bool correct = true;
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
bool wire_cut = (cut_wires & (1<<i)) != 0;
|
||||
if (solution[i] && !wire_cut) {
|
||||
correct = false;
|
||||
}
|
||||
}
|
||||
if (correct) {
|
||||
return;
|
||||
} else {
|
||||
strike("Not all wires cut!");
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
10
main/steps/step6.h
Normal file
10
main/steps/step6.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef STEP_6_H
|
||||
#define STEP_6_H
|
||||
|
||||
#include "wires_puzzle.h"
|
||||
#include "../drivers/all.h"
|
||||
#include "../helper.h"
|
||||
|
||||
void step6(void);
|
||||
|
||||
#endif /* STEP_6_H */
|
||||
13
main/steps/tetris_resources/CMakeLists.txt
Normal file
13
main/steps/tetris_resources/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
set(SOURCES
|
||||
"bg.c"
|
||||
"clear.c"
|
||||
"db.c"
|
||||
"green.c"
|
||||
"lb.c"
|
||||
"orange.c"
|
||||
"purple.c"
|
||||
"red.c"
|
||||
"yellow.c"
|
||||
)
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES})
|
||||
677
main/steps/tetris_resources/bg.c
Normal file
677
main/steps/tetris_resources/bg.c
Normal file
File diff suppressed because one or more lines are too long
69
main/steps/tetris_resources/clear.c
Normal file
69
main/steps/tetris_resources/clear.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_CLEAR
|
||||
#define LV_ATTRIBUTE_IMG_CLEAR
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_CLEAR uint8_t clear_map[] = {
|
||||
0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0xdb, 0xde, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xbb, 0xde, 0x5d, 0xef, 0x7d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x9e, 0xf7, 0x7e, 0xf7, 0x7e, 0xf7, 0x5d, 0xef, 0xba, 0xd6, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0xef, 0xb7, 0xbd, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xb7, 0xbd, 0xf8, 0xc5, 0x38, 0xc6, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0x18, 0xc6, 0xd7, 0xbd, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf7, 0xbd, 0x38, 0xc6, 0x79, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xba, 0xd6, 0xba, 0xd6, 0x9a, 0xd6, 0x9a, 0xd6, 0x7a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0xf8, 0xc5, 0x59, 0xce, 0x7a, 0xd6, 0x9a, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0x9a, 0xd6, 0x59, 0xce, 0x18, 0xc6, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x18, 0xc6, 0x79, 0xce, 0x9a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xfb, 0xde, 0xdb, 0xde, 0xbb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x7a, 0xd6, 0xbb, 0xde, 0xdb, 0xde, 0xfb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0xfc, 0xe6, 0xdb, 0xde, 0xdb, 0xde, 0x9a, 0xd6, 0x59, 0xce, 0x75, 0xad, 0xba, 0xd6,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x39, 0xce, 0x9a, 0xd6, 0xdb, 0xde, 0xfb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x3c, 0xe7, 0x1c, 0xe7, 0xfc, 0xe6, 0xfb, 0xde, 0xba, 0xd6, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6, 0xbb, 0xde, 0x75, 0xad, 0x59, 0xce, 0x9a, 0xd6, 0xfb, 0xde, 0xfc, 0xe6, 0x3c, 0xe7, 0x3d, 0xef, 0x3d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xfc, 0xe6, 0xbb, 0xde, 0x7a, 0xd6, 0x75, 0xad, 0xba, 0xd6,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0xfc, 0xe6, 0x1c, 0xe7, 0x5d, 0xef, 0x5d, 0xef, 0x7d, 0xef, 0x5d, 0xef, 0x3c, 0xe7, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde, 0xbb, 0xde, 0x75, 0xad, 0x79, 0xce, 0xbb, 0xde, 0x1c, 0xe7, 0x1c, 0xe7, 0x5d, 0xef, 0x7e, 0xf7, 0x9e, 0xf7, 0x7d, 0xef, 0x3d, 0xef, 0x1c, 0xe7, 0xdb, 0xde, 0x9a, 0xd6, 0x75, 0xad, 0xbb, 0xde,
|
||||
0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde, 0xba, 0xd6, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0x75, 0xad, 0xbb, 0xde,
|
||||
0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xbb, 0xde, 0xba, 0xd6, 0xbb, 0xde, 0xba, 0xd6, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t clear = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 160,
|
||||
.header.h = 16,
|
||||
.data_size = 7680,
|
||||
.data = clear_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/db.c
Normal file
69
main/steps/tetris_resources/db.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_DB
|
||||
#define LV_ATTRIBUTE_IMG_DB
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DB uint8_t db_map[] = {
|
||||
0x00, 0x00, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x00, 0x00,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x33, 0x54, 0x33, 0x54, 0x33, 0x54, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x13, 0x33, 0xb5, 0x4b, 0xd5, 0x4b, 0xf6, 0x4b, 0xf7, 0x4b, 0x17, 0x4c, 0x17, 0x4c, 0x17, 0x4c, 0xf6, 0x4b, 0xf6, 0x4b, 0xb5, 0x4b, 0x13, 0x33, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xf1, 0x01, 0xd5, 0x4b, 0xdd, 0xce, 0xbb, 0x9d, 0xb9, 0x64, 0xb9, 0x64, 0xb9, 0x64, 0xb9, 0x64, 0xb9, 0x64, 0x98, 0x64, 0xf6, 0x4b, 0xf1, 0x01, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xf1, 0x01, 0x53, 0x02, 0x95, 0x02, 0x58, 0x54, 0x7b, 0x85, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x79, 0x54, 0xb5, 0x02, 0x74, 0x02, 0x32, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x32, 0x02, 0x95, 0x02, 0xd6, 0x02, 0xf7, 0x02, 0x38, 0x03, 0x38, 0x03, 0x59, 0x03, 0x38, 0x03, 0x18, 0x03, 0xf7, 0x02, 0xb5, 0x02, 0x53, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x53, 0x02, 0xb5, 0x02, 0xf7, 0x02, 0x18, 0x03, 0x59, 0x03, 0x59, 0x03, 0x59, 0x03, 0x59, 0x03, 0x38, 0x03, 0x18, 0x03, 0xd6, 0x02, 0x74, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x74, 0x02, 0xd6, 0x02, 0x38, 0x03, 0x59, 0x03, 0x7a, 0x03, 0x9a, 0x03, 0xbb, 0x03, 0x9a, 0x03, 0x7a, 0x03, 0x59, 0x03, 0x17, 0x03, 0xb5, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0x95, 0x02, 0xf7, 0x02, 0x59, 0x03, 0x5a, 0x03, 0xbb, 0x03, 0xbb, 0x03, 0xbb, 0x03, 0xbb, 0x03, 0x9a, 0x03, 0x59, 0x03, 0x38, 0x03, 0xb6, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xb5, 0x02, 0x18, 0x03, 0x7a, 0x03, 0xbb, 0x03, 0xfc, 0x03, 0xfc, 0x03, 0x1d, 0x04, 0xfc, 0x03, 0xbb, 0x03, 0x9b, 0x03, 0x59, 0x03, 0xd7, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xb6, 0x02, 0x38, 0x03, 0x9a, 0x03, 0xbb, 0x03, 0xfc, 0x03, 0x1d, 0x04, 0x1d, 0x04, 0x1d, 0x04, 0xfc, 0x03, 0xbb, 0x03, 0x59, 0x03, 0xf7, 0x02, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xd6, 0x02, 0x59, 0x03, 0xbb, 0x03, 0xfc, 0x03, 0x3d, 0x04, 0x5e, 0x04, 0x7e, 0x04, 0x5e, 0x04, 0x1d, 0x04, 0xfc, 0x03, 0x7a, 0x03, 0x18, 0x03, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xd7, 0x02, 0x59, 0x03, 0xbb, 0x03, 0xfc, 0x03, 0x5e, 0x04, 0x9f, 0x04, 0xbf, 0x04, 0x7f, 0x04, 0x1d, 0x04, 0xfc, 0x03, 0x9a, 0x03, 0x18, 0x03, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x13, 0x33, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0x13, 0x33,
|
||||
0x00, 0x00, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x13, 0x33, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t db = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = db_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/green.c
Normal file
69
main/steps/tetris_resources/green.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_GREEN
|
||||
#define LV_ATTRIBUTE_IMG_GREEN
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_GREEN uint8_t green_map[] = {
|
||||
0x00, 0x00, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0x00, 0x00,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xe9, 0x34, 0xe9, 0x34, 0xe9, 0x34, 0xe9, 0x34, 0x09, 0x35, 0x09, 0x35, 0x09, 0x35, 0x09, 0x35, 0xe9, 0x34, 0xe9, 0x34, 0xe9, 0x34, 0xe9, 0x34, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xe9, 0x34, 0x2c, 0x4d, 0x6c, 0x4d, 0x8c, 0x4d, 0xac, 0x4d, 0xcc, 0x4d, 0xcc, 0x4d, 0xcc, 0x4d, 0x8c, 0x4d, 0x8c, 0x4d, 0x2c, 0x4d, 0xc9, 0x34, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x44, 0x04, 0x6c, 0x4d, 0x7a, 0xcf, 0xb5, 0x9e, 0x4f, 0x66, 0x4f, 0x66, 0x4f, 0x66, 0x4f, 0x66, 0x2f, 0x66, 0x0f, 0x66, 0x8c, 0x4d, 0x44, 0x04, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x44, 0x04, 0xc4, 0x04, 0x25, 0x05, 0x0e, 0x56, 0xd3, 0x86, 0x91, 0x6e, 0x91, 0x6e, 0x91, 0x6e, 0x2e, 0x56, 0x65, 0x05, 0x05, 0x05, 0x84, 0x04, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xa4, 0x04, 0x25, 0x05, 0xa5, 0x05, 0xc6, 0x05, 0x06, 0x06, 0x26, 0x06, 0x26, 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x05, 0x65, 0x05, 0xe5, 0x04, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xc4, 0x04, 0x65, 0x05, 0xc6, 0x05, 0xe6, 0x05, 0x46, 0x06, 0x47, 0x06, 0x47, 0x06, 0x47, 0x06, 0x26, 0x06, 0x06, 0x06, 0x85, 0x05, 0x05, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x05, 0x05, 0xa5, 0x05, 0x06, 0x06, 0x47, 0x06, 0x87, 0x06, 0xa7, 0x06, 0xa7, 0x06, 0xa7, 0x06, 0x67, 0x06, 0x46, 0x06, 0xe6, 0x05, 0x65, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x45, 0x05, 0xc6, 0x05, 0x46, 0x06, 0x67, 0x06, 0xa7, 0x06, 0xc8, 0x06, 0xe8, 0x06, 0xc8, 0x06, 0x87, 0x06, 0x67, 0x06, 0x06, 0x06, 0x85, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x65, 0x05, 0x06, 0x06, 0x87, 0x06, 0xa7, 0x06, 0x08, 0x07, 0x08, 0x07, 0x28, 0x07, 0x28, 0x07, 0xe8, 0x06, 0xa7, 0x06, 0x46, 0x06, 0xc6, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0x65, 0x05, 0x06, 0x06, 0xa7, 0x06, 0xc7, 0x06, 0x28, 0x07, 0x48, 0x07, 0x48, 0x07, 0x48, 0x07, 0x08, 0x07, 0xc7, 0x06, 0x47, 0x06, 0xc6, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xa5, 0x05, 0x46, 0x06, 0xc7, 0x06, 0x08, 0x07, 0x69, 0x07, 0x89, 0x07, 0xa9, 0x07, 0x89, 0x07, 0x28, 0x07, 0x08, 0x07, 0x87, 0x06, 0xe6, 0x05, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xa5, 0x05, 0x47, 0x06, 0xe8, 0x06, 0x08, 0x07, 0x89, 0x07, 0xc9, 0x07, 0xea, 0x07, 0xa9, 0x07, 0x48, 0x07, 0x08, 0x07, 0x87, 0x06, 0x06, 0x06, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0xa9, 0x34, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa3, 0x03, 0xa9, 0x34,
|
||||
0x00, 0x00, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0xa9, 0x34, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t green = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = green_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/lb.c
Normal file
69
main/steps/tetris_resources/lb.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_LB
|
||||
#define LV_ATTRIBUTE_IMG_LB
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LB uint8_t lb_map[] = {
|
||||
0x00, 0x00, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x00, 0x00,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x73, 0x34, 0x73, 0x34, 0x73, 0x34, 0x73, 0x34, 0x94, 0x34, 0x94, 0x34, 0x94, 0x34, 0x94, 0x34, 0x73, 0x34, 0x73, 0x34, 0x73, 0x34, 0x73, 0x34, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x73, 0x34, 0xd5, 0x4c, 0x15, 0x4d, 0x36, 0x4d, 0x37, 0x4d, 0x57, 0x4d, 0x57, 0x4d, 0x57, 0x4d, 0x36, 0x4d, 0x16, 0x4d, 0xf5, 0x4c, 0x53, 0x34, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0xb1, 0x03, 0x15, 0x4d, 0x3d, 0xcf, 0x7b, 0x9e, 0xd9, 0x65, 0xd9, 0x65, 0xf9, 0x65, 0xf9, 0x65, 0xd9, 0x65, 0xd8, 0x65, 0x36, 0x4d, 0xb1, 0x03, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0xb1, 0x03, 0x33, 0x04, 0x95, 0x04, 0xb8, 0x55, 0x7b, 0x86, 0x3a, 0x6e, 0x3a, 0x6e, 0x3a, 0x6e, 0xd9, 0x55, 0xd5, 0x04, 0x54, 0x04, 0xf2, 0x03, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x12, 0x04, 0x95, 0x04, 0x16, 0x05, 0x37, 0x05, 0x78, 0x05, 0x78, 0x05, 0x99, 0x05, 0x78, 0x05, 0x58, 0x05, 0x37, 0x05, 0xd5, 0x04, 0x53, 0x04, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x33, 0x04, 0xb5, 0x04, 0x37, 0x05, 0x58, 0x05, 0x99, 0x05, 0xb9, 0x05, 0xb9, 0x05, 0xb9, 0x05, 0x78, 0x05, 0x58, 0x05, 0xf6, 0x04, 0x74, 0x04, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x74, 0x04, 0x16, 0x05, 0x78, 0x05, 0xb9, 0x05, 0xda, 0x05, 0xfa, 0x05, 0x1b, 0x06, 0xfa, 0x05, 0xda, 0x05, 0x99, 0x05, 0x37, 0x05, 0xb5, 0x04, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0xb5, 0x04, 0x37, 0x05, 0x99, 0x05, 0xda, 0x05, 0x1b, 0x06, 0x3b, 0x06, 0x3b, 0x06, 0x3b, 0x06, 0xfa, 0x05, 0xd9, 0x05, 0x78, 0x05, 0xd6, 0x04, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0xb5, 0x04, 0x58, 0x05, 0xda, 0x05, 0x1b, 0x06, 0x5c, 0x06, 0x7c, 0x06, 0x7d, 0x06, 0x7c, 0x06, 0x3b, 0x06, 0xfb, 0x05, 0x99, 0x05, 0x17, 0x05, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0xd6, 0x04, 0x78, 0x05, 0xfa, 0x05, 0x1b, 0x06, 0x7c, 0x06, 0x9d, 0x06, 0x9d, 0x06, 0x9d, 0x06, 0x5c, 0x06, 0x1b, 0x06, 0xb9, 0x05, 0x37, 0x05, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x16, 0x05, 0x99, 0x05, 0x1b, 0x06, 0x5c, 0x06, 0xbd, 0x06, 0xde, 0x06, 0xfe, 0x06, 0xde, 0x06, 0x9d, 0x06, 0x5c, 0x06, 0xda, 0x05, 0x58, 0x05, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x17, 0x05, 0xb9, 0x05, 0x3b, 0x06, 0x7c, 0x06, 0xde, 0x06, 0x1f, 0x07, 0x3f, 0x07, 0x1f, 0x07, 0x9d, 0x06, 0x7c, 0x06, 0xfa, 0x05, 0x58, 0x05, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x53, 0x34, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x2f, 0x03, 0x53, 0x34,
|
||||
0x00, 0x00, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x53, 0x34, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t lb = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = lb_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/orange.c
Normal file
69
main/steps/tetris_resources/orange.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_ORANGE
|
||||
#define LV_ATTRIBUTE_IMG_ORANGE
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_ORANGE uint8_t orange_map[] = {
|
||||
0x00, 0x00, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x00, 0x00,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x86, 0x9b, 0x86, 0x9b, 0x86, 0x9b, 0x86, 0x9b, 0xa6, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3, 0x86, 0x9b, 0x86, 0x9b, 0x86, 0x9b, 0x86, 0x9b, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x86, 0x9b, 0x09, 0xac, 0x49, 0xac, 0x49, 0xb4, 0x69, 0xbc, 0x69, 0xbc, 0x89, 0xbc, 0x69, 0xbc, 0x49, 0xb4, 0x49, 0xb4, 0x09, 0xac, 0x86, 0x9b, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x80, 0x8a, 0x29, 0xac, 0xf9, 0xee, 0xf3, 0xdd, 0x0c, 0xcd, 0x0c, 0xcd, 0x2c, 0xcd, 0x0c, 0xcd, 0x0c, 0xcd, 0xec, 0xc4, 0x49, 0xb4, 0x80, 0x8a, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x80, 0x8a, 0xe0, 0x9a, 0x40, 0xab, 0xca, 0xc4, 0xd0, 0xdd, 0x6d, 0xd5, 0x6d, 0xd5, 0x6d, 0xd5, 0xea, 0xcc, 0x60, 0xab, 0x00, 0xa3, 0xc0, 0x92, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0xc0, 0x92, 0x40, 0xab, 0x80, 0xb3, 0xa0, 0xbb, 0xe0, 0xc3, 0xe0, 0xc3, 0x00, 0xcc, 0xe0, 0xc3, 0xe0, 0xc3, 0xa0, 0xbb, 0x60, 0xab, 0xe0, 0x9a, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0xe0, 0x9a, 0x40, 0xab, 0xa0, 0xbb, 0xe0, 0xc3, 0x00, 0xcc, 0x20, 0xcc, 0x20, 0xcc, 0x20, 0xcc, 0xe0, 0xc3, 0xe0, 0xc3, 0x80, 0xb3, 0x20, 0xa3, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x20, 0xa3, 0x80, 0xb3, 0xe0, 0xc3, 0x20, 0xcc, 0x40, 0xd4, 0x60, 0xd4, 0x80, 0xdc, 0x60, 0xd4, 0x40, 0xd4, 0x20, 0xcc, 0xc0, 0xbb, 0x40, 0xab, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x40, 0xab, 0xa0, 0xbb, 0x00, 0xcc, 0x40, 0xd4, 0x80, 0xdc, 0xa0, 0xdc, 0xa0, 0xdc, 0xa0, 0xdc, 0x60, 0xd4, 0x40, 0xcc, 0xe0, 0xc3, 0x60, 0xb3, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x40, 0xab, 0xe0, 0xc3, 0x40, 0xd4, 0x80, 0xdc, 0xc0, 0xe4, 0xe0, 0xe4, 0xe0, 0xec, 0xe0, 0xe4, 0xa0, 0xdc, 0x80, 0xdc, 0x00, 0xcc, 0xa0, 0xbb, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x60, 0xb3, 0xe0, 0xc3, 0x60, 0xd4, 0x80, 0xdc, 0xe0, 0xe4, 0x00, 0xed, 0x00, 0xed, 0x00, 0xed, 0xc0, 0xe4, 0x80, 0xdc, 0x20, 0xcc, 0xa0, 0xbb, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x80, 0xb3, 0x20, 0xcc, 0x80, 0xdc, 0xc0, 0xe4, 0x20, 0xed, 0x40, 0xf5, 0x40, 0xf5, 0x40, 0xf5, 0x00, 0xed, 0xc0, 0xe4, 0x40, 0xd4, 0xc0, 0xc3, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0xa0, 0xbb, 0x20, 0xcc, 0xa0, 0xdc, 0xe0, 0xe4, 0x40, 0xf5, 0x60, 0xfd, 0x80, 0xfd, 0x60, 0xfd, 0x00, 0xed, 0xe0, 0xe4, 0x60, 0xd4, 0xe0, 0xc3, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x66, 0x9b, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x20, 0x7a, 0x66, 0x9b,
|
||||
0x00, 0x00, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x66, 0x9b, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t orange = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = orange_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/purple.c
Normal file
69
main/steps/tetris_resources/purple.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_PURPLE
|
||||
#define LV_ATTRIBUTE_IMG_PURPLE
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PURPLE uint8_t purple_map[] = {
|
||||
0x00, 0x00, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0x00, 0x00,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x93, 0x89, 0x93, 0x89, 0x93, 0x89, 0x93, 0x89, 0x94, 0x89, 0x94, 0x89, 0x94, 0x89, 0x94, 0x89, 0x93, 0x89, 0x93, 0x89, 0x93, 0x89, 0x93, 0x89, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x73, 0x81, 0x55, 0x92, 0x55, 0x9a, 0x56, 0x9a, 0x57, 0xa2, 0x57, 0xa2, 0x57, 0xa2, 0x57, 0xa2, 0x56, 0x9a, 0x56, 0x9a, 0x55, 0x92, 0x73, 0x81, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x11, 0x68, 0x35, 0x9a, 0x5d, 0xe6, 0xdb, 0xcc, 0x19, 0xb3, 0x19, 0xb3, 0x19, 0xb3, 0x19, 0xb3, 0x19, 0xb3, 0x18, 0xb3, 0x56, 0x9a, 0x11, 0x68, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x11, 0x68, 0x13, 0x78, 0x15, 0x80, 0x98, 0xaa, 0x1b, 0xcc, 0x5a, 0xbb, 0x5a, 0xc3, 0x5a, 0xbb, 0x99, 0xb2, 0x15, 0x88, 0x14, 0x80, 0x12, 0x70, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x12, 0x78, 0x15, 0x80, 0x16, 0x90, 0x17, 0x98, 0x18, 0xa0, 0x18, 0xa0, 0x19, 0xa0, 0x18, 0xa0, 0x18, 0x98, 0x17, 0x98, 0x15, 0x88, 0x13, 0x78, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x13, 0x78, 0x15, 0x88, 0x17, 0x98, 0x18, 0x98, 0x19, 0xa0, 0x19, 0xa8, 0x19, 0xa8, 0x19, 0xa8, 0x18, 0xa0, 0x18, 0x98, 0x16, 0x90, 0x14, 0x80, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x14, 0x80, 0x16, 0x90, 0x18, 0xa0, 0x19, 0xa8, 0x1a, 0xa8, 0x1a, 0xb0, 0x1b, 0xb0, 0x1a, 0xb0, 0x1a, 0xa8, 0x19, 0xa0, 0x17, 0x98, 0x15, 0x88, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x15, 0x88, 0x17, 0x98, 0x19, 0xa0, 0x1a, 0xa8, 0x1b, 0xb0, 0x1b, 0xb8, 0x1b, 0xb8, 0x1b, 0xb8, 0x1a, 0xb0, 0x19, 0xa8, 0x18, 0xa0, 0x16, 0x88, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x15, 0x88, 0x18, 0x98, 0x1a, 0xa8, 0x1b, 0xb0, 0x1c, 0xb8, 0x1c, 0xc0, 0x1d, 0xc0, 0x1c, 0xc0, 0x1b, 0xb8, 0x1b, 0xb0, 0x19, 0xa0, 0x17, 0x90, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x16, 0x88, 0x18, 0xa0, 0x1a, 0xb0, 0x1b, 0xb0, 0x1c, 0xc0, 0x1d, 0xc0, 0x1d, 0xc0, 0x1d, 0xc0, 0x1c, 0xb8, 0x1b, 0xb0, 0x19, 0xa8, 0x17, 0x98, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x16, 0x90, 0x19, 0xa0, 0x1b, 0xb0, 0x1c, 0xb8, 0x1d, 0xc8, 0x1e, 0xc8, 0x1e, 0xd0, 0x1e, 0xc8, 0x1d, 0xc0, 0x1c, 0xb8, 0x1a, 0xa8, 0x18, 0x98, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x17, 0x90, 0x19, 0xa8, 0x1b, 0xb8, 0x1c, 0xc0, 0x1e, 0xc8, 0x1f, 0xd0, 0x1f, 0xd8, 0x1f, 0xd0, 0x1d, 0xc0, 0x1c, 0xc0, 0x1a, 0xb0, 0x18, 0x98, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0xb3, 0x81, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0x0f, 0x58, 0xb3, 0x81,
|
||||
0x00, 0x00, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0xb3, 0x81, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t purple = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = purple_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/red.c
Normal file
69
main/steps/tetris_resources/red.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_RED
|
||||
#define LV_ATTRIBUTE_IMG_RED
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_RED uint8_t red_map[] = {
|
||||
0x00, 0x00, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0x00, 0x00,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x86, 0x99, 0x86, 0x99, 0x86, 0x99, 0x86, 0x99, 0x86, 0xa1, 0x86, 0xa1, 0x86, 0xa1, 0x86, 0xa1, 0x86, 0x99, 0x86, 0x99, 0x86, 0x99, 0x86, 0x99, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x66, 0x99, 0x49, 0xaa, 0x49, 0xaa, 0x49, 0xb2, 0x49, 0xba, 0x49, 0xba, 0x49, 0xba, 0x49, 0xba, 0x49, 0xb2, 0x49, 0xb2, 0x49, 0xaa, 0x66, 0x99, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x88, 0x29, 0xaa, 0x59, 0xee, 0xd3, 0xdc, 0x0c, 0xcb, 0x0c, 0xcb, 0x0c, 0xcb, 0x0c, 0xcb, 0x0c, 0xcb, 0x0c, 0xc3, 0x49, 0xb2, 0x00, 0x88, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x88, 0x00, 0x98, 0x00, 0xa8, 0x8a, 0xc2, 0x10, 0xdc, 0x4d, 0xd3, 0x4d, 0xd3, 0x4d, 0xd3, 0x8a, 0xca, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x90, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x90, 0x00, 0xa8, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc8, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xa8, 0x00, 0x98, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x98, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc0, 0x00, 0xc8, 0x00, 0xc8, 0x00, 0xc8, 0x00, 0xc8, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xb0, 0x00, 0xa0, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xa0, 0x00, 0xb0, 0x00, 0xc0, 0x00, 0xc8, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xb8, 0x00, 0xa8, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, 0x00, 0xb0, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xa8, 0x00, 0xc0, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe8, 0x00, 0xe0, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xc8, 0x00, 0xb8, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xb0, 0x00, 0xc0, 0x00, 0xd0, 0x00, 0xd8, 0x00, 0xe0, 0x00, 0xe8, 0x00, 0xe8, 0x00, 0xe8, 0x00, 0xe0, 0x00, 0xd8, 0x00, 0xc8, 0x00, 0xb8, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xb0, 0x00, 0xc8, 0x00, 0xd8, 0x00, 0xe0, 0x00, 0xe8, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xe8, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0xd8, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xe8, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0x78, 0xa6, 0x99,
|
||||
0xa6, 0x99, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0xa6, 0x99,
|
||||
0x00, 0x00, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t red = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = red_map,
|
||||
};
|
||||
69
main/steps/tetris_resources/yellow.c
Normal file
69
main/steps/tetris_resources/yellow.c
Normal file
@ -0,0 +1,69 @@
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_YELLOW
|
||||
#define LV_ATTRIBUTE_IMG_YELLOW
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_YELLOW uint8_t yellow_map[] = {
|
||||
0x00, 0x00, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x00, 0x00,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0xa8, 0x9c, 0xa8, 0xa4, 0xa8, 0xa4, 0xa8, 0xa4, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x88, 0x9c, 0xcb, 0xa4, 0xeb, 0xac, 0x0b, 0xad, 0x2b, 0xb5, 0x2b, 0xb5, 0x2b, 0xb5, 0x2b, 0xb5, 0x0b, 0xad, 0x0b, 0xad, 0xcb, 0xa4, 0x88, 0x9c, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x20, 0x94, 0xeb, 0xac, 0x18, 0xef, 0x33, 0xce, 0xae, 0xc5, 0xae, 0xc5, 0xae, 0xc5, 0xae, 0xc5, 0x8e, 0xbd, 0x8e, 0xbd, 0x0b, 0xad, 0x20, 0x94, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x20, 0x94, 0x60, 0x9c, 0xa0, 0xa4, 0x8c, 0xbd, 0x31, 0xd6, 0xef, 0xcd, 0xef, 0xcd, 0xef, 0xcd, 0x8c, 0xbd, 0xc0, 0xac, 0x80, 0xa4, 0x40, 0x94, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x40, 0x9c, 0xa0, 0xa4, 0x00, 0xad, 0x20, 0xb5, 0x40, 0xbd, 0x40, 0xbd, 0x60, 0xbd, 0x40, 0xbd, 0x40, 0xbd, 0x20, 0xb5, 0xc0, 0xac, 0x80, 0x9c, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x60, 0x9c, 0xc0, 0xac, 0x20, 0xb5, 0x20, 0xb5, 0x60, 0xbd, 0x80, 0xc5, 0x80, 0xc5, 0x80, 0xc5, 0x40, 0xbd, 0x40, 0xbd, 0xe0, 0xac, 0x80, 0xa4, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x80, 0xa4, 0x00, 0xad, 0x40, 0xbd, 0x80, 0xc5, 0xa0, 0xcd, 0xc0, 0xcd, 0xc0, 0xcd, 0xc0, 0xcd, 0xa0, 0xc5, 0x60, 0xc5, 0x20, 0xb5, 0xc0, 0xac, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0xc0, 0xa4, 0x20, 0xb5, 0x60, 0xbd, 0x80, 0xc5, 0xc0, 0xcd, 0xe0, 0xd5, 0x00, 0xd6, 0xe0, 0xd5, 0xc0, 0xcd, 0x80, 0xc5, 0x40, 0xbd, 0xe0, 0xac, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0xc0, 0xac, 0x40, 0xbd, 0xa0, 0xcd, 0xc0, 0xcd, 0x20, 0xde, 0x20, 0xde, 0x40, 0xe6, 0x40, 0xde, 0x00, 0xd6, 0xc0, 0xcd, 0x60, 0xbd, 0x00, 0xb5, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0xe0, 0xac, 0x40, 0xbd, 0xc0, 0xcd, 0xe0, 0xd5, 0x40, 0xde, 0x60, 0xe6, 0x60, 0xe6, 0x60, 0xe6, 0x20, 0xde, 0xe0, 0xd5, 0x80, 0xc5, 0x20, 0xb5, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x00, 0xad, 0x60, 0xc5, 0xe0, 0xd5, 0x20, 0xde, 0x80, 0xee, 0xa0, 0xee, 0xc0, 0xf6, 0xa0, 0xee, 0x40, 0xe6, 0x20, 0xde, 0xa0, 0xcd, 0x20, 0xb5, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0x00, 0xb5, 0x80, 0xc5, 0x00, 0xd6, 0x20, 0xde, 0xa0, 0xee, 0xe0, 0xf6, 0x00, 0xff, 0xe0, 0xf6, 0x60, 0xe6, 0x20, 0xde, 0xc0, 0xcd, 0x40, 0xbd, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x88, 0x9c, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0xa0, 0x83, 0x88, 0x9c,
|
||||
0x00, 0x00, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x88, 0x9c, 0x00, 0x00,
|
||||
/*alpha channel*/
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t yellow = {
|
||||
.header.cf = LV_IMG_CF_RGB565A8,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 16,
|
||||
.header.h = 16,
|
||||
.data_size = 768,
|
||||
.data = yellow_map,
|
||||
};
|
||||
419
main/steps/wires_puzzle.cpp
Normal file
419
main/steps/wires_puzzle.cpp
Normal file
@ -0,0 +1,419 @@
|
||||
#include "wires_puzzle.h"
|
||||
|
||||
const static char* WIRES_FILE_PATH = MOUNT_POINT "/wires.txt";
|
||||
const static char* TAG = "wires_puzzle";
|
||||
|
||||
static int color_name_len[NUM_COLORS] = {
|
||||
3, // red
|
||||
6, // yellow
|
||||
5, // green
|
||||
4, // blue
|
||||
5, // black
|
||||
5, // white
|
||||
};
|
||||
|
||||
static int max(int n0, int n1, int n2, int n3, int n4, int n5);
|
||||
|
||||
void wires_to_string(WireColor* wires, char* out_wires_string) {
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
switch (wires[i]) {
|
||||
case WireColor::wire_red:
|
||||
out_wires_string[i] = 'r';
|
||||
break;
|
||||
case WireColor::wire_yellow:
|
||||
out_wires_string[i] = 'y';
|
||||
break;
|
||||
case WireColor::wire_green:
|
||||
out_wires_string[i] = 'g';
|
||||
break;
|
||||
case WireColor::wire_blue:
|
||||
out_wires_string[i] = 'b';
|
||||
break;
|
||||
case WireColor::wire_black:
|
||||
out_wires_string[i] = 'k';
|
||||
break;
|
||||
case WireColor::wire_white:
|
||||
out_wires_string[i] = 'w';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void cut_to_string(bool* cut, char* out_cut_string) {
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (cut[i]) {
|
||||
out_cut_string[i] = 'c';
|
||||
} else {
|
||||
out_cut_string[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void string_to_wires(char* wires_string, WireColor* out_wires) {
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
switch (wires_string[i]) {
|
||||
case 'r':
|
||||
out_wires[i] = WireColor::wire_red;
|
||||
break;
|
||||
case 'y':
|
||||
out_wires[i] = WireColor::wire_yellow;
|
||||
break;
|
||||
case 'g':
|
||||
out_wires[i] = WireColor::wire_green;
|
||||
break;
|
||||
case 'b':
|
||||
out_wires[i] = WireColor::wire_blue;
|
||||
break;
|
||||
case 'k':
|
||||
out_wires[i] = WireColor::wire_black;
|
||||
break;
|
||||
case 'w':
|
||||
out_wires[i] = WireColor::wire_white;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void save_wires_to_sd_card(WireColor* wires) {
|
||||
FILE* f = fopen(WIRES_FILE_PATH, "w");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open wires file to write");
|
||||
}
|
||||
char wires_string[NUM_WIRES+1] = {0};
|
||||
wires_to_string(wires, wires_string);
|
||||
fprintf(f, wires_string);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void load_wires_from_sd_card(WireColor* out_wires) {
|
||||
FILE* f = fopen(WIRES_FILE_PATH, "r");
|
||||
if (f == NULL) {
|
||||
ESP_LOGW(TAG, "Failed to read wires file. Generating new wires");
|
||||
generate_new_wires(out_wires);
|
||||
save_wires_to_sd_card(out_wires);
|
||||
return;
|
||||
}
|
||||
char wires_string[NUM_WIRES+1] = {0};
|
||||
fgets(wires_string, sizeof(wires_string), f);
|
||||
fclose(f);
|
||||
|
||||
string_to_wires(wires_string, out_wires);
|
||||
}
|
||||
|
||||
/// @brief Fills the array with 6 random WiresColors.
|
||||
/// @param wires and array of len >= 6 to be populated with random colors
|
||||
void generate_new_wires(WireColor* wires) {
|
||||
for (int w = 0; w < NUM_WIRES; w++) {
|
||||
// roughly evenly distributed
|
||||
uint32_t rand = esp_random() % NUM_COLORS;
|
||||
wires[w] = (WireColor)(rand);
|
||||
}
|
||||
|
||||
bool cuts[NUM_WIRES] = {0};
|
||||
solve_wires(wires, cuts);
|
||||
int num_cuts = 0;
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (cuts[i]) num_cuts++;
|
||||
}
|
||||
|
||||
// regenerate if there are less than 3 cuts.
|
||||
if (num_cuts < 3) {
|
||||
return generate_new_wires(wires);
|
||||
}
|
||||
}
|
||||
|
||||
void solve_wires(WireColor* wires, bool* out_cut) {
|
||||
bool debug = false;
|
||||
|
||||
// by default, don't cut any wires
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
out_cut[i] = false;
|
||||
}
|
||||
|
||||
// Find all positions of all wire colors
|
||||
int red_pos[NUM_WIRES + 1] = {0};
|
||||
int red_pos_len = 0;
|
||||
int yellow_pos[NUM_WIRES + 1] = {0};
|
||||
int yellow_pos_len = 0;
|
||||
int green_pos[NUM_WIRES + 1] = {0};
|
||||
int green_pos_len = 0;
|
||||
int blue_pos[NUM_WIRES + 1] = {0};
|
||||
int blue_pos_len = 0;
|
||||
int black_pos[NUM_WIRES + 1] = {0};
|
||||
int black_pos_len = 0;
|
||||
int white_pos[NUM_WIRES + 1] = {0};
|
||||
int white_pos_len = 0;
|
||||
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (wires[i] == WireColor::wire_red) {
|
||||
red_pos[red_pos_len++] = i;
|
||||
} else if (wires[i] == WireColor::wire_yellow) {
|
||||
yellow_pos[yellow_pos_len++] = i;
|
||||
} else if (wires[i] == WireColor::wire_green) {
|
||||
green_pos[green_pos_len++] = i;
|
||||
} else if (wires[i] == WireColor::wire_blue) {
|
||||
blue_pos[blue_pos_len++] = i;
|
||||
} else if (wires[i] == WireColor::wire_black) {
|
||||
black_pos[black_pos_len++] = i;
|
||||
} else if (wires[i] == WireColor::wire_white) {
|
||||
white_pos[white_pos_len++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
int* list_pos[NUM_COLORS] = {
|
||||
red_pos,
|
||||
yellow_pos,
|
||||
green_pos,
|
||||
blue_pos,
|
||||
black_pos,
|
||||
white_pos
|
||||
};
|
||||
int list_pos_len[NUM_COLORS] = {
|
||||
red_pos_len,
|
||||
yellow_pos_len,
|
||||
green_pos_len,
|
||||
blue_pos_len,
|
||||
black_pos_len,
|
||||
white_pos_len
|
||||
};
|
||||
|
||||
// CUT CHECKS
|
||||
|
||||
// 1. cut the second wire of all most common colors
|
||||
int max_len = max(red_pos_len, yellow_pos_len, green_pos_len, blue_pos_len, black_pos_len, white_pos_len);
|
||||
if (max_len >= 2) {
|
||||
for (int i = 0; i < NUM_COLORS; i++) {
|
||||
if (list_pos_len[i] == max_len) {
|
||||
int idx = list_pos[i][1];
|
||||
out_cut[idx] = true;
|
||||
if (debug) {
|
||||
printf("C1. cutting %d\n", idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. cut the first wire if it is green or white
|
||||
if (wires[0] == WireColor::wire_green || wires[0] == WireColor::wire_white) {
|
||||
out_cut[0] = true;
|
||||
if (debug) {
|
||||
printf("C2. cutting %d\n", 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. cut blue wires in even positions (odd indexes)
|
||||
for (int i = 1; i < NUM_WIRES; i += 2) {
|
||||
if (wires[i] == WireColor::wire_blue) {
|
||||
out_cut[i] = true;
|
||||
if (debug) {
|
||||
printf("C3. cutting %d\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. cut black and yellow wires next to black and yellow wires
|
||||
for (int i = 0; i < NUM_WIRES-1; i++) {
|
||||
if (
|
||||
(wires[i] == WireColor::wire_yellow || wires[i] == WireColor::wire_black) &&
|
||||
(wires[i+1] == WireColor::wire_yellow || wires[i+1] == WireColor::wire_black)
|
||||
) {
|
||||
out_cut[i] = true;
|
||||
out_cut[i+1] = true;
|
||||
if (debug) {
|
||||
printf("C4. cutting %d, %d\n", i, i+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. cut the last green wire next to a yellow or white wire
|
||||
for (int green_idx = green_pos_len-1; green_idx >= 0; green_idx--) {
|
||||
int pos = green_pos[green_idx];
|
||||
if (
|
||||
wires[pos-1] == WireColor::wire_yellow ||
|
||||
wires[pos-1] == WireColor::wire_white ||
|
||||
wires[pos+1] == WireColor::wire_yellow ||
|
||||
wires[pos+1] == WireColor::wire_white
|
||||
) {
|
||||
out_cut[pos] = true;
|
||||
if (debug) {
|
||||
printf("C5. cutting %d\n", pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. cut all white wires if there is a red wire in the 5th position
|
||||
if (wires[4] == WireColor::wire_red) {
|
||||
for (int white_idx = 0; white_idx < white_pos_len; white_idx++) {
|
||||
out_cut[white_pos[white_idx]] = true;
|
||||
if (debug) {
|
||||
printf("C6. cutting %d\n", white_pos[white_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. cut the first black wire if there are more white wires than green wires
|
||||
if (white_pos_len > green_pos_len && black_pos_len > 0) {
|
||||
out_cut[black_pos[0]] = true;
|
||||
if (debug) {
|
||||
printf("C7. cutting %d\n", black_pos[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. cut all wires in an alternating pattern of 2 colors at least 4 wires long
|
||||
for (int i = 0; i < NUM_WIRES-3; i++) {
|
||||
if (
|
||||
wires[i] == wires[i+2] &&
|
||||
wires[i+1] == wires[i+3] &&
|
||||
wires[i] == wires[i+1]
|
||||
) {
|
||||
out_cut[i] = true;
|
||||
out_cut[i+1] = true;
|
||||
out_cut[i+2] = true;
|
||||
out_cut[i+3] = true;
|
||||
if (debug) {
|
||||
printf("C8. cutting %d, %d, %d, %d\n", i, i+1, i+2, i+3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9. cut any wires if their position matches the number of letters in the color's name
|
||||
for (int i = 0; i < NUM_WIRES; i++) {
|
||||
if (color_name_len[wires[i]] == i+1) {
|
||||
out_cut[i] = true;
|
||||
if (debug) {
|
||||
printf("C9. cutting %d\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 10. cut all red wires if there are no more than 2 wires of the same color
|
||||
if (max_len <= 2) {
|
||||
for (int i = 0; i < red_pos_len; i++) {
|
||||
out_cut[red_pos[i]] = true;
|
||||
if (debug) {
|
||||
printf("C10. cutting %d\n", red_pos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 11. cut the last wire if it is the same color as the first wire
|
||||
if (wires[0] == wires[NUM_WIRES-1]) {
|
||||
out_cut[NUM_WIRES-1] = true;
|
||||
if (debug) {
|
||||
printf("C11. cutting %d\n", NUM_WIRES-1);
|
||||
}
|
||||
}
|
||||
|
||||
// 12. cut any wire adjacent to both a yellow and blue wire
|
||||
for (int i = 0; i < NUM_WIRES-2; i++) {
|
||||
if (
|
||||
(wires[i] == WireColor::wire_yellow && wires[i+2] == WireColor::wire_blue) ||
|
||||
(wires[i] == WireColor::wire_blue && wires[i+2] == WireColor::wire_yellow)
|
||||
) {
|
||||
out_cut[i+1] = true;
|
||||
if (debug) {
|
||||
printf("C12. cutting %d\n", i+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEVER CUT
|
||||
|
||||
// 1. never cut blue wires next to red or green wires
|
||||
for (int i = 0; i < blue_pos_len; i++) {
|
||||
int pos = blue_pos[i];
|
||||
if (
|
||||
wires[pos-1] == WireColor::wire_red ||
|
||||
wires[pos-1] == WireColor::wire_green ||
|
||||
wires[pos+1] == WireColor::wire_red ||
|
||||
wires[pos+1] == WireColor::wire_green
|
||||
) {
|
||||
out_cut[pos] = false;
|
||||
if (debug) {
|
||||
printf("N1. Never cutting %d\n", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. never cut white wires if there is at least one red, black and green wire
|
||||
if (red_pos_len > 0 && green_pos_len > 0 && black_pos_len > 0) {
|
||||
for (int i = 0; i < white_pos_len; i++) {
|
||||
out_cut[white_pos[i]] = false;
|
||||
if (debug) {
|
||||
printf("N2. Never cutting %d\n", white_pos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. never cut red or black wires in the 4th or 7th positions
|
||||
if (wires[3] == WireColor::wire_red || wires[3] == WireColor::wire_black) {
|
||||
out_cut[3] = false;
|
||||
if (debug) {
|
||||
printf("N3. Never cutting %d\n", 3);
|
||||
}
|
||||
}
|
||||
if (wires[6] == WireColor::wire_red || wires[6] == WireColor::wire_black) {
|
||||
out_cut[6] = false;
|
||||
if (debug) {
|
||||
printf("N3. Never cutting %d\n", 6);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. never cut wires that have the same color on both sides of it
|
||||
for (int i = 0; i < NUM_WIRES-2; i++) {
|
||||
if (wires[i] == wires[i+2]) {
|
||||
out_cut[i+1] = false;
|
||||
if (debug) {
|
||||
printf("N4. Never cutting %d\n", i+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. never cut a wire in the 1st, 2nd, or 3rd position if it is the same color as the wire in the 4th position.
|
||||
if (wires[0] == wires[3]) {
|
||||
out_cut[0] = false;
|
||||
if (debug) {
|
||||
printf("N5. Never cutting %d\n", 0);
|
||||
}
|
||||
}
|
||||
if (wires[1] == wires[3]) {
|
||||
out_cut[1] = false;
|
||||
if (debug) {
|
||||
printf("N5. Never cutting %d\n", 1);
|
||||
}
|
||||
}
|
||||
if (wires[2] == wires[3]) {
|
||||
out_cut[2] = false;
|
||||
if (debug) {
|
||||
printf("N5. Never cutting %d\n", 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. never cut a blue or green wire in the 8th postion
|
||||
if (wires[7] == WireColor::wire_blue || wires[7] == WireColor::wire_green) {
|
||||
out_cut[7] = false;
|
||||
if (debug) {
|
||||
printf("N6. Never cutting %d\n", 7);
|
||||
}
|
||||
}
|
||||
|
||||
// 7. never cut a wire in the 5th position if there are no yellow wires
|
||||
if (yellow_pos_len == 0) {
|
||||
out_cut[4] = false;
|
||||
if (debug) {
|
||||
printf("N7 Never cutting %d\n", 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int max(int n0, int n1, int n2, int n3, int n4, int n5) {
|
||||
int max = n0;
|
||||
|
||||
if (n1 > max) max = n1;
|
||||
if (n2 > max) max = n2;
|
||||
if (n3 > max) max = n3;
|
||||
if (n4 > max) max = n4;
|
||||
if (n5 > max) max = n5;
|
||||
|
||||
return max;
|
||||
}
|
||||
39
main/steps/wires_puzzle.h
Normal file
39
main/steps/wires_puzzle.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef WIRES_PUZZLE_H
|
||||
#define WIRES_PUZZLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_random.h>
|
||||
#include "drivers/sd.h"
|
||||
|
||||
#define NUM_COLORS 6
|
||||
#define NUM_WIRES 8
|
||||
|
||||
typedef enum {
|
||||
wire_red = 0,
|
||||
wire_yellow = 1,
|
||||
wire_green = 2,
|
||||
wire_blue = 3,
|
||||
wire_black = 4,
|
||||
wire_white = 5,
|
||||
} WireColor;
|
||||
|
||||
void solve_wires(WireColor* wires, bool* out_cut);
|
||||
void generate_new_wires(WireColor* wires);
|
||||
|
||||
void cut_to_string(bool* cut, char* out_cut_string);
|
||||
void wires_to_string(WireColor* wires, char* out_wires_string);
|
||||
void string_to_wires(char* wires_string, WireColor* out_wires);
|
||||
|
||||
void save_wires_to_sd_card(WireColor* wires);
|
||||
void load_wires_from_sd_card(WireColor* out_wires);
|
||||
|
||||
/// @brief Fills the array with 6 random WiresColors.
|
||||
/// @param wires the array of len >= 6 to be populated with random colors
|
||||
void generate_new_wires(WireColor* wires);
|
||||
|
||||
/// @brief Solves the wires puzzle, setting `out_cut` according to which wires are cut.
|
||||
/// @param wires the array of len >= 6 as input to the solver
|
||||
/// @param out_cut the output array of len >= 6 for which wires to cut
|
||||
void solve_wires(WireColor* wires, bool* out_cut);
|
||||
|
||||
#endif /* WIRES_PUZZLE_H */
|
||||
8
partitions.csv
Normal file
8
partitions.csv
Normal file
@ -0,0 +1,8 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs,data,nvs,0x9000,0x6000,,
|
||||
phy_init,data,phy,0xf000,0x1000,,
|
||||
ota,data,ota,0x10000,0x2000,,
|
||||
factory,app,factory,0x20000,2M,,
|
||||
ota0,app,ota_0,0x220000,2M,,
|
||||
ota1,app,ota_1,0x420000,2M,,
|
||||
|
55
pytest_hello_world.py
Normal file
55
pytest_hello_world.py
Normal file
@ -0,0 +1,55 @@
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import hashlib
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
from pytest_embedded_qemu.app import QemuApp
|
||||
from pytest_embedded_qemu.dut import QemuDut
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
def test_hello_world(dut: IdfDut, log_minimum_free_heap_size: Callable[..., None]) -> None:
|
||||
dut.expect('Hello world!')
|
||||
log_minimum_free_heap_size()
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@idf_parametrize('target', ['linux'], indirect=['target'])
|
||||
def test_hello_world_linux(dut: IdfDut) -> None:
|
||||
dut.expect('Hello world!')
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@pytest.mark.macos_shell
|
||||
@idf_parametrize('target', ['linux'], indirect=['target'])
|
||||
def test_hello_world_macos(dut: IdfDut) -> None:
|
||||
dut.expect('Hello world!')
|
||||
|
||||
|
||||
def verify_elf_sha256_embedding(app: QemuApp, sha256_reported: str) -> None:
|
||||
sha256 = hashlib.sha256()
|
||||
with open(app.elf_file, 'rb') as f:
|
||||
sha256.update(f.read())
|
||||
sha256_expected = sha256.hexdigest()
|
||||
|
||||
logging.info(f'ELF file SHA256: {sha256_expected}')
|
||||
logging.info(f'ELF file SHA256 (reported by the app): {sha256_reported}')
|
||||
|
||||
# the app reports only the first several hex characters of the SHA256, check that they match
|
||||
if not sha256_expected.startswith(sha256_reported):
|
||||
raise ValueError('ELF file SHA256 mismatch')
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@pytest.mark.qemu
|
||||
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
|
||||
def test_hello_world_host(app: QemuApp, dut: QemuDut) -> None:
|
||||
sha256_reported = dut.expect(r'ELF file SHA256:\s+([a-f0-9]+)').group(1).decode('utf-8')
|
||||
verify_elf_sha256_embedding(app, sha256_reported)
|
||||
|
||||
dut.expect('Hello world!')
|
||||
0
sdkconfig.ci
Normal file
0
sdkconfig.ci
Normal file
Loading…
Reference in New Issue
Block a user