Compare commits
23 Commits
event_base
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dcd4f5bb62 | |||
| da781c23f1 | |||
| 0caf28c86a | |||
| a8d3a61cf6 | |||
| ee80bdb3eb | |||
| aadd924bd0 | |||
| 53f58a3133 | |||
| 92d448020c | |||
| 202b926eb7 | |||
| 8bddceca66 | |||
| cfc307ee72 | |||
| a9e44145f0 | |||
| 15e8630a88 | |||
| 4e75aeb69c | |||
| 4a47558ef6 | |||
| e27fb6f015 | |||
| 1267d1356d | |||
| 9cc1a93e73 | |||
| 72ff92b444 | |||
| e1ce43d57c | |||
| ee30fce074 | |||
| 219f1f1507 | |||
| 10648045a3 |
@ -4,20 +4,25 @@ set(SOURCES
|
|||||||
"TM1640/TM1640.cpp"
|
"TM1640/TM1640.cpp"
|
||||||
"SparkFunBQ27441/SparkFunBQ27441.cpp"
|
"SparkFunBQ27441/SparkFunBQ27441.cpp"
|
||||||
"esp_lcd_ili9488/esp_lcd_ili9488.c"
|
"esp_lcd_ili9488/esp_lcd_ili9488.c"
|
||||||
# "bottom_half.cpp"
|
"bottom_half.cpp"
|
||||||
"event_based_bottom_half.cpp"
|
|
||||||
"inputs.cpp"
|
|
||||||
"char_lcd.cpp"
|
"char_lcd.cpp"
|
||||||
|
"game_info.cpp"
|
||||||
"game_timer.cpp"
|
"game_timer.cpp"
|
||||||
|
"hwdata.cpp"
|
||||||
"i2c_lcd_pcf8574.c"
|
"i2c_lcd_pcf8574.c"
|
||||||
|
"i2c.cpp"
|
||||||
"leds.cpp"
|
"leds.cpp"
|
||||||
|
"nvs.cpp"
|
||||||
|
"perh.cpp"
|
||||||
"power.cpp"
|
"power.cpp"
|
||||||
"sd.cpp"
|
"sd.cpp"
|
||||||
"speaker.cpp"
|
"speaker.cpp"
|
||||||
"sseg.cpp"
|
"sseg.cpp"
|
||||||
|
"starcode.cpp"
|
||||||
"state_tracking.cpp"
|
"state_tracking.cpp"
|
||||||
"tft.cpp"
|
"tft.cpp"
|
||||||
"wires.cpp"
|
"wires.cpp"
|
||||||
|
"wlvgl.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES})
|
target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES})
|
||||||
|
|||||||
@ -32,6 +32,7 @@ Arduino Uno (any 'duino should do)
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "SparkFunBQ27441.h"
|
#include "SparkFunBQ27441.h"
|
||||||
|
#include "../i2c.h"
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
************************** Initialization Functions *************************
|
************************** Initialization Functions *************************
|
||||||
@ -710,7 +711,9 @@ bool BQ27441::writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data,
|
|||||||
int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
|
int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
|
||||||
{
|
{
|
||||||
int16_t timeout = BQ72441_I2C_TIMEOUT;
|
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);
|
i2c_master_write_read_device(BQ72441_I2C_NUM, _deviceAddress, &subAddress, 1, dest, count, timeout);
|
||||||
|
xSemaphoreGive(main_i2c_mutex);
|
||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +729,9 @@ uint16_t BQ27441::i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count
|
|||||||
w_buff[i+1] = src[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);
|
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;
|
return ret == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
static const char *TAG = "driver_all";
|
|
||||||
static void init_i2c();
|
|
||||||
|
|
||||||
void init_drivers() {
|
void init_drivers() {
|
||||||
init_i2c();
|
init_i2c();
|
||||||
// init char_lcd so we can use it to report other initialization errors.
|
// init char_lcd so we can use it to report other initialization errors.
|
||||||
init_lcd();
|
init_lcd();
|
||||||
|
init_nvs();
|
||||||
|
init_star_code_system();
|
||||||
// init the bottom half so that we can get user input.
|
// init the bottom half so that we can get user input.
|
||||||
init_bottom_half();
|
init_bottom_half();
|
||||||
init_sd();
|
init_sd();
|
||||||
@ -16,36 +15,6 @@ void init_drivers() {
|
|||||||
init_tft();
|
init_tft();
|
||||||
init_leds();
|
init_leds();
|
||||||
init_power_board();
|
init_power_board();
|
||||||
}
|
|
||||||
|
set_lcd_header_enabled(true);
|
||||||
/// @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
|
|
||||||
static 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 = {
|
|
||||||
.clk_speed = 100*1000,
|
|
||||||
},
|
|
||||||
.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf));
|
|
||||||
ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0));
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "i2c initialized!");
|
|
||||||
}
|
}
|
||||||
@ -1,17 +1,20 @@
|
|||||||
#ifndef ALL_H
|
#ifndef ALL_H
|
||||||
#define ALL_H
|
#define ALL_H
|
||||||
|
|
||||||
#define PIN_I2C_SDA GPIO_NUM_7
|
|
||||||
#define PIN_I2C_SCL GPIO_NUM_15
|
|
||||||
|
|
||||||
#include "char_lcd.h"
|
|
||||||
#include "bottom_half.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 "sd.h"
|
||||||
#include "speaker.h"
|
#include "speaker.h"
|
||||||
#include "game_timer.h"
|
#include "sseg.h"
|
||||||
#include "drivers/tft.h"
|
#include "starcode.h"
|
||||||
#include "drivers/leds.h"
|
#include "state_tracking.h"
|
||||||
#include "drivers/power.h"
|
#include "tft.h"
|
||||||
|
#include "wires.h"
|
||||||
|
|
||||||
void init_drivers();
|
void init_drivers();
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "bottom_half.h"
|
#include "bottom_half.h"
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include "state_tracking.h"
|
#include "state_tracking.h"
|
||||||
|
#include "starcode.h"
|
||||||
|
|
||||||
static const char *TAG = "bottom_half";
|
static const char *TAG = "bottom_half";
|
||||||
|
|
||||||
@ -45,8 +46,14 @@ static bool replay_handler(const char* event, char* arg) {
|
|||||||
void init_bottom_half() {
|
void init_bottom_half() {
|
||||||
ESP_LOGI(TAG, "Initializing bottom half...");
|
ESP_LOGI(TAG, "Initializing bottom half...");
|
||||||
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(BOTTOM_PIN_INTERUPT, GPIO_MODE_INPUT));
|
gpio_config_t int_conf = {
|
||||||
ESP_ERROR_CHECK(gpio_set_pull_mode(BOTTOM_PIN_INTERUPT, GPIO_PULLUP_ONLY));
|
.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.
|
// TODO: do interupt stuff.
|
||||||
// ESP_ERROR_CHECK(gpio_intr_enable(BOTTOM_PIN_INTERUPT));
|
// ESP_ERROR_CHECK(gpio_intr_enable(BOTTOM_PIN_INTERUPT));
|
||||||
@ -69,20 +76,27 @@ void init_bottom_half() {
|
|||||||
|
|
||||||
static uint8_t receive_delta(void) {
|
static uint8_t receive_delta(void) {
|
||||||
uint8_t reg = 1;
|
uint8_t reg = 1;
|
||||||
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)));
|
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];
|
return buf[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void receive_keypad(void) {
|
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;
|
uint8_t reg = 2;
|
||||||
buf[0] = 0;
|
xSemaphoreTake(main_i2c_mutex, portMAX_DELAY);
|
||||||
buf[1] = 0;
|
esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS));
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(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 new_keypad_state = buf[0] | (buf[1] << 8);
|
||||||
|
|
||||||
uint16_t just_pressed = new_keypad_state & ~keypad_state;
|
uint16_t just_pressed = new_keypad_state & ~keypad_state;
|
||||||
keypad_pressed |= just_pressed;
|
|
||||||
if (is_state_tracking() && just_pressed) {
|
if (is_state_tracking() && just_pressed) {
|
||||||
char buf[6];
|
char buf[6];
|
||||||
sprintf(buf, "%d", just_pressed);
|
sprintf(buf, "%d", just_pressed);
|
||||||
@ -90,19 +104,29 @@ static void receive_keypad(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t just_released = ~new_keypad_state & keypad_state;
|
uint16_t just_released = ~new_keypad_state & keypad_state;
|
||||||
keypad_released |= just_released;
|
|
||||||
if (is_state_tracking() && just_released) {
|
if (is_state_tracking() && just_released) {
|
||||||
char buf[6];
|
char buf[6];
|
||||||
sprintf(buf, "%d", just_released);
|
sprintf(buf, "%d", just_released);
|
||||||
event_occured("KP_RELEASE", buf);
|
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;
|
keypad_state = new_keypad_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void receive_button_switch(void) {
|
static void receive_button_switch(void) {
|
||||||
uint8_t reg = 3;
|
uint8_t reg = 3;
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS)));
|
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_button_state = buf[1] & 0xF;
|
||||||
uint8_t new_switch_state = (~buf[0]) & 0xF;
|
uint8_t new_switch_state = (~buf[0]) & 0xF;
|
||||||
uint8_t new_switch_touch_state = (buf[1] >> 4) & 0xF;
|
uint8_t new_switch_touch_state = (buf[1] >> 4) & 0xF;
|
||||||
@ -168,7 +192,9 @@ static void receive_button_switch(void) {
|
|||||||
static void receive_touch(void) {
|
static void receive_touch(void) {
|
||||||
uint8_t reg = 4;
|
uint8_t reg = 4;
|
||||||
buf[0] = 0;
|
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)));
|
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 new_touch_state = buf[0] != 0;
|
||||||
|
|
||||||
bool just_pressed = new_touch_state & !touch_state;
|
bool just_pressed = new_touch_state & !touch_state;
|
||||||
@ -190,12 +216,10 @@ static void poll_bottom_task(void *arg) {
|
|||||||
// TODO: if using an interupt, switch this to use a queue
|
// TODO: if using an interupt, switch this to use a queue
|
||||||
while (1) {
|
while (1) {
|
||||||
bool new_data = gpio_get_level(BOTTOM_PIN_INTERUPT) == 0;
|
bool new_data = gpio_get_level(BOTTOM_PIN_INTERUPT) == 0;
|
||||||
// bool new_data = 1;
|
|
||||||
if (new_data) {
|
if (new_data) {
|
||||||
uint8_t delta = receive_delta();
|
uint8_t delta = receive_delta();
|
||||||
// ESP_LOGI(_TAG, "delta: %d", delta);
|
// ESP_LOGI(_TAG, "delta: %d", delta);
|
||||||
// if (delta == 0) ESP_LOGW(TAG, "delta pin was low, but delta register returned 0");
|
if (delta == 0) ESP_LOGW(TAG, "delta pin was low, but delta register returned 0");
|
||||||
if (delta != 0) ESP_LOGI(TAG, "delta!");
|
|
||||||
|
|
||||||
if (delta & (1 << DELTA_BIT_KP)) receive_keypad();
|
if (delta & (1 << DELTA_BIT_KP)) receive_keypad();
|
||||||
if (delta & (1 << DELTA_BIT_BUTTON_SWITCH)) receive_button_switch();
|
if (delta & (1 << DELTA_BIT_BUTTON_SWITCH)) receive_button_switch();
|
||||||
@ -219,7 +243,8 @@ void clear_all_pressed_released(void) {
|
|||||||
touch_released = 0;
|
touch_released = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _take_key(KeypadKey* kp, uint16_t* keypad_bitfield) {
|
// 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++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
int bit_selector = (1 << i);
|
int bit_selector = (1 << i);
|
||||||
if ((*keypad_bitfield) & bit_selector) {
|
if ((*keypad_bitfield) & bit_selector) {
|
||||||
@ -235,10 +260,10 @@ static bool _take_key(KeypadKey* kp, uint16_t* keypad_bitfield) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool get_keypad_pressed(KeypadKey* kp) {
|
bool get_keypad_pressed(KeypadKey* kp) {
|
||||||
return _take_key(kp, &keypad_pressed);
|
return take_key(kp, &keypad_pressed);
|
||||||
}
|
}
|
||||||
bool get_keypad_released(KeypadKey* kp) {
|
bool get_keypad_released(KeypadKey* kp) {
|
||||||
return _take_key(kp, &keypad_released);
|
return take_key(kp, &keypad_released);
|
||||||
}
|
}
|
||||||
|
|
||||||
char char_of_keypad_key(KeypadKey kp) {
|
char char_of_keypad_key(KeypadKey kp) {
|
||||||
|
|||||||
@ -14,22 +14,22 @@
|
|||||||
|
|
||||||
/// @brief An enum for the possible keypad buttons.
|
/// @brief An enum for the possible keypad buttons.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
k1 = 0,
|
kd = 0,
|
||||||
k4 = 1,
|
pound = 1,
|
||||||
k7 = 2,
|
k0 = 2,
|
||||||
star = 3,
|
star = 3,
|
||||||
k2 = 4,
|
kc = 4,
|
||||||
k5 = 5,
|
k9 = 5,
|
||||||
k8 = 6,
|
k8 = 6,
|
||||||
k0 = 7,
|
k7 = 7,
|
||||||
k3 = 8,
|
kb = 8,
|
||||||
k6 = 9,
|
k6 = 9,
|
||||||
k9 = 10,
|
k5 = 10,
|
||||||
pound = 11,
|
k4 = 11,
|
||||||
ka = 12,
|
ka = 12,
|
||||||
kb = 13,
|
k3 = 13,
|
||||||
kc = 14,
|
k2 = 14,
|
||||||
kd = 15,
|
k1 = 15,
|
||||||
} KeypadKey;
|
} KeypadKey;
|
||||||
|
|
||||||
/// @brief An enum for the possible buttons.
|
/// @brief An enum for the possible buttons.
|
||||||
@ -126,6 +126,14 @@ bool get_touch_pressed();
|
|||||||
/// @return true if the touch sensor was just released
|
/// @return true if the touch sensor was just released
|
||||||
bool get_touch_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
|
// TODO: add touch sensor for switch
|
||||||
|
|
||||||
#endif /* BOTTOM_HALF_HPP */
|
#endif /* BOTTOM_HALF_HPP */
|
||||||
@ -4,64 +4,66 @@
|
|||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include "state_tracking.h"
|
#include "state_tracking.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include "power.h"
|
||||||
|
#include "starcode.h"
|
||||||
|
#include "game_info.h"
|
||||||
|
|
||||||
i2c_lcd_pcf8574_handle_t lcd;
|
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 *TAG = "char_lcd";
|
||||||
|
static const char* EMPTY_ROW = " ";
|
||||||
|
|
||||||
static char buf[65];
|
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) {
|
static bool replay_handler(const char* event, char* arg) {
|
||||||
if (strcmp(event, "LCD_CLEAR") == 0) {
|
if (strcmp(event, "LCD_CLEAR") == 0) {
|
||||||
lcd_clear();
|
lcd_clear();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_CURSOR") == 0) {
|
else if (strcmp(event, "LCD_SET_DISPLAY") == 0) {
|
||||||
char* col_str = strtok(arg, ",");
|
|
||||||
char* row_str = strtok(NULL, ",");
|
|
||||||
uint32_t col = atoi(col_str);
|
|
||||||
uint32_t row = atoi(row_str);
|
|
||||||
lcd_set_cursor_pos(col, row);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(event, "LCD_SET_DISPLAY") == 0) {
|
|
||||||
lcd_set_display(strcmp(arg, "true") == 0);
|
lcd_set_display(strcmp(arg, "true") == 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_CURSOR_VIS") == 0) {
|
else if (strcmp(event, "LCD_CURSOR_VIS") == 0) {
|
||||||
lcd_set_cursor_vis(strcmp(arg, "true") == 0);
|
lcd_set_cursor_vis(strcmp(arg, "true") == 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_CURSOR_BLINK") == 0) {
|
else if (strcmp(event, "LCD_CURSOR_BLINK") == 0) {
|
||||||
lcd_set_cursor_blink(strcmp(arg, "true") == 0);
|
lcd_set_cursor_blink(strcmp(arg, "true") == 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) {
|
else if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) {
|
||||||
lcd_scroll_display_left();
|
lcd_scroll_display_left();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) {
|
else if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) {
|
||||||
lcd_scroll_display_right();
|
lcd_scroll_display_right();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) {
|
else if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) {
|
||||||
lcd_left_to_right();
|
lcd_left_to_right();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) {
|
else if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) {
|
||||||
lcd_right_to_left();
|
lcd_right_to_left();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_AUTOSCROLL") == 0) {
|
else if (strcmp(event, "LCD_AUTOSCROLL") == 0) {
|
||||||
lcd_set_autoscroll(strcmp(arg, "true") == 0);
|
lcd_set_autoscroll(strcmp(arg, "true") == 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_BACKLIGHT") == 0) {
|
else if (strcmp(event, "LCD_BACKLIGHT") == 0) {
|
||||||
uint32_t brightness = atoi(arg);
|
lcd_set_backlight(strcmp(arg, "true") == 0);
|
||||||
lcd_set_backlight(brightness);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_CREATE_CHAR") == 0) {
|
else if (strcmp(event, "LCD_CREATE_CHAR") == 0) {
|
||||||
char* location_str = strtok(arg, ",");
|
char* location_str = strtok(arg, ",");
|
||||||
uint8_t location = atoi(location_str);
|
uint8_t location = atoi(location_str);
|
||||||
|
|
||||||
@ -72,152 +74,176 @@ static bool replay_handler(const char* event, char* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lcd_create_char(location, charmap);
|
lcd_create_char(location, charmap);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (strcmp(event, "LCD_WRITE") == 0) {
|
else if (strcmp(event, "LCD_PRINT") == 0) {
|
||||||
uint8_t value = atoi(arg);
|
char* str = strtok(arg, ",");
|
||||||
lcd_write(value);
|
uint8_t row = atoi(str);
|
||||||
return true;
|
str = strtok(NULL, ",");
|
||||||
}
|
uint8_t col = atoi(str);
|
||||||
if (strcmp(event, "LCD_PRINT") == 0) {
|
// get remaining part of string.
|
||||||
|
str = strtok(NULL, "");
|
||||||
|
|
||||||
// TODO: handle \r and \n
|
// TODO: handle \r and \n
|
||||||
lcd_print(arg);
|
lcd_print(row, col, str);
|
||||||
return true;
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_lcd() {
|
void init_lcd() {
|
||||||
ESP_LOGI(TAG, "Initializing LCD...");
|
ESP_LOGI(TAG, "Initializing LCD...");
|
||||||
|
|
||||||
|
lcd_mutex = xSemaphoreCreateMutex();
|
||||||
|
assert(lcd_mutex != NULL);
|
||||||
|
|
||||||
lcd_init(&lcd, LCD_ADDR, CHAR_LCD_I2C_NUM);
|
lcd_init(&lcd, LCD_ADDR, CHAR_LCD_I2C_NUM);
|
||||||
lcd_begin(&lcd, LCD_COLS, LCD_ROWS);
|
lcd_begin(&lcd, LCD_COLS, LCD_ROWS);
|
||||||
|
|
||||||
lcd_set_backlight(&lcd, 255);
|
lcd_set_backlight_to(&lcd, 1);
|
||||||
|
|
||||||
register_replay_fn(replay_handler);
|
register_replay_fn(replay_handler);
|
||||||
|
|
||||||
|
xTaskCreate(monitor_battery_task, "bat_monitor", 1024*2, nullptr, 0, nullptr);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "LCD initialized!");
|
ESP_LOGI(TAG, "LCD initialized!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_clear() {
|
void lcd_clear(bool no_lock) {
|
||||||
lcd_clear(&lcd);
|
if (!header_enabled) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
|
lcd_clear(&lcd);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_CLEAR", NULL);
|
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_cursor_home() {
|
void lcd_set_display(bool display, bool no_lock) {
|
||||||
lcd_home(&lcd);
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
|
||||||
event_occured("LCD_CURSOR", "0,0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void lcd_set_cursor_pos(uint8_t col, uint8_t row) {
|
|
||||||
lcd_set_cursor(&lcd, col, row);
|
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
|
||||||
sprintf(buf, "%d,%d", col, row);
|
|
||||||
event_occured("LCD_CURSOR", buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void lcd_set_display(bool display) {
|
|
||||||
if (display) {
|
if (display) {
|
||||||
lcd_display(&lcd);
|
lcd_display(&lcd);
|
||||||
} else {
|
} else {
|
||||||
lcd_no_display(&lcd);
|
lcd_no_display(&lcd);
|
||||||
}
|
}
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_SET_DISPLAY", display ? "true" : "false");
|
event_occured("LCD_SET_DISPLAY", display ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_set_cursor_vis(bool cursor) {
|
void lcd_set_cursor_vis(bool cursor, bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
lcd_cursor(&lcd);
|
lcd_cursor(&lcd);
|
||||||
} else {
|
} else {
|
||||||
lcd_no_cursor(&lcd);
|
lcd_no_cursor(&lcd);
|
||||||
}
|
}
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
|
cursor_visible = cursor;
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_CURSOR_VIS", cursor ? "true" : "false");
|
event_occured("LCD_CURSOR_VIS", cursor ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_set_cursor_blink(bool blink) {
|
void lcd_set_cursor_blink(bool blink, bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
if (blink) {
|
if (blink) {
|
||||||
lcd_blink(&lcd);
|
lcd_blink(&lcd);
|
||||||
} else {
|
} else {
|
||||||
lcd_no_blink(&lcd);
|
lcd_no_blink(&lcd);
|
||||||
}
|
}
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_CURSOR_BLINK", blink ? "true" : "false");
|
event_occured("LCD_CURSOR_BLINK", blink ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_scroll_display_left() {
|
void lcd_scroll_display_left(bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
lcd_scroll_display_left(&lcd);
|
lcd_scroll_display_left(&lcd);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_SCROLL_DISPLAY_LEFT", NULL);
|
event_occured("LCD_SCROLL_DISPLAY_LEFT", NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void lcd_scroll_display_right() {
|
void lcd_scroll_display_right(bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
lcd_scroll_display_right(&lcd);
|
lcd_scroll_display_right(&lcd);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_SCROLL_DISPLAY_RIGHT", NULL);
|
event_occured("LCD_SCROLL_DISPLAY_RIGHT", NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_left_to_right() {
|
void lcd_left_to_right(bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
lcd_left_to_right(&lcd);
|
lcd_left_to_right(&lcd);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_LEFT_TO_RIGHT", NULL);
|
event_occured("LCD_LEFT_TO_RIGHT", NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void lcd_right_to_left() {
|
void lcd_right_to_left(bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
lcd_right_to_left(&lcd);
|
lcd_right_to_left(&lcd);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_RIGHT_TO_LEFT", NULL);
|
event_occured("LCD_RIGHT_TO_LEFT", NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_set_autoscroll(bool autoscroll) {
|
void lcd_set_autoscroll(bool autoscroll, bool no_lock) {
|
||||||
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
if (autoscroll) {
|
if (autoscroll) {
|
||||||
lcd_autoscroll(&lcd);
|
lcd_autoscroll(&lcd);
|
||||||
} else {
|
} else {
|
||||||
lcd_no_autoscroll(&lcd);
|
lcd_no_autoscroll(&lcd);
|
||||||
}
|
}
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
event_occured("LCD_AUTOSCROLL", autoscroll ? "true" : "false");
|
event_occured("LCD_AUTOSCROLL", autoscroll ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_set_backlight(uint8_t brightness) {
|
void lcd_set_backlight(bool backlight, bool no_lock) {
|
||||||
lcd_set_backlight(&lcd, brightness);
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
|
lcd_set_backlight_to(&lcd, backlight);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
sprintf(buf, "%d", brightness);
|
sprintf(buf, "%d", backlight);
|
||||||
event_occured("LCD_BACKLIGHT", buf);
|
event_occured("LCD_BACKLIGHT", backlight ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_create_char(uint8_t location, uint8_t* charmap) {
|
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);
|
lcd_create_char(&lcd, location, charmap);
|
||||||
|
if (!no_lock) xSemaphoreGive(lcd_mutex);
|
||||||
|
|
||||||
if (is_state_tracking()) {
|
if (is_state_tracking()) {
|
||||||
snprintf(buf, 65,
|
snprintf(buf, sizeof(buf),
|
||||||
"%d,%d,%d,%d,%d,%d,%d,%d,%d", location,
|
"%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]
|
charmap[0], charmap[1], charmap[2], charmap[3], charmap[4], charmap[5], charmap[6], charmap[7]
|
||||||
);
|
);
|
||||||
@ -225,25 +251,88 @@ void lcd_create_char(uint8_t location, uint8_t* charmap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write(uint8_t value) {
|
void lcd_print(uint8_t row, uint8_t col, const char* str, bool no_lock) {
|
||||||
lcd_write(&lcd, value);
|
if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY);
|
||||||
|
lcd_set_cursor(&lcd, col, row);
|
||||||
if (is_state_tracking()) {
|
|
||||||
sprintf(buf, "%d", value);
|
|
||||||
event_occured("LCD_WRITE", buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void lcd_print(const char* str) {
|
|
||||||
lcd_print(&lcd, str);
|
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()) {
|
if (is_state_tracking()) {
|
||||||
// TODO: handle \r and \n
|
// TODO: handle \r and \n and others
|
||||||
event_occured("LCD_PRINT", str);
|
snprintf(buf, sizeof(buf), "%d,%d,%s", row, col, str);
|
||||||
|
event_occured("LCD_PRINT", buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_print(uint8_t col, uint8_t row, const char* str) {
|
void set_lcd_header_enabled(bool enable) {
|
||||||
lcd_set_cursor_pos(col, row);
|
bool old_header_enabled = header_enabled;
|
||||||
lcd_print(str);
|
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;
|
||||||
}
|
}
|
||||||
@ -6,56 +6,91 @@
|
|||||||
#define CHAR_LCD_I2C_NUM I2C_NUM_0
|
#define CHAR_LCD_I2C_NUM I2C_NUM_0
|
||||||
|
|
||||||
#define LCD_ADDR 0x27
|
#define LCD_ADDR 0x27
|
||||||
#define LCD_COLS 20
|
|
||||||
#define LCD_ROWS 4
|
#define LCD_ROWS 4
|
||||||
|
#define LCD_COLS 20
|
||||||
|
|
||||||
/// Initializes the 2004 Character LCD
|
/// @brief Initializes the 2004 Character LCD
|
||||||
void init_lcd();
|
void init_lcd();
|
||||||
|
|
||||||
/// Clear the LCD
|
/// @brief Clear the LCD
|
||||||
void lcd_clear();
|
void lcd_clear(bool no_lock = false);
|
||||||
|
|
||||||
/// Move cursor to home position
|
/// @brief Move cursor to home position
|
||||||
void lcd_cursor_home();
|
void lcd_cursor_home(bool no_lock = false);
|
||||||
|
|
||||||
/// Set cursor position
|
/// @brief Turn the display on/off
|
||||||
void lcd_set_cursor_pos(uint8_t col, uint8_t row);
|
void lcd_set_display(bool display, bool no_lock = false);
|
||||||
|
|
||||||
/// Turn the display on/off
|
/// @brief Turn the cursor's visibility on/off
|
||||||
void lcd_set_display(bool display);
|
void lcd_set_cursor_vis(bool cursor, bool no_lock = false);
|
||||||
|
|
||||||
/// Turn the cursor's visibility on/off
|
/// @brief Turn blinking cursor on/off
|
||||||
void lcd_set_cursor_vis(bool cursor);
|
void lcd_set_cursor_blink(bool blink, bool no_lock = false);
|
||||||
|
|
||||||
/// Turn blinking cursor on/off
|
/// @brief Scroll the display left
|
||||||
void lcd_set_cursor_blink(bool blink);
|
void lcd_scroll_display_left(bool no_lock = false);
|
||||||
|
/// @brief Scroll the display right
|
||||||
|
void lcd_scroll_display_right(bool no_lock = false);
|
||||||
|
|
||||||
/// Scroll the display left
|
/// @brief Set the text to flows automatically left to right
|
||||||
void lcd_scroll_display_left();
|
void lcd_left_to_right(bool no_lock = false);
|
||||||
/// Scroll the display right
|
/// @brief Set the text to flows automatically right to left
|
||||||
void lcd_scroll_display_right();
|
void lcd_right_to_left(bool no_lock = false);
|
||||||
|
|
||||||
/// Set the text to flows automatically left to right
|
/// @brief Turn on/off autoscroll
|
||||||
void lcd_left_to_right();
|
void lcd_set_autoscroll(bool autoscroll, bool no_lock = false);
|
||||||
/// Set the text to flows automatically right to left
|
|
||||||
void lcd_right_to_left();
|
|
||||||
|
|
||||||
// Turn on/off autoscroll
|
/// @brief Set backlight brightness
|
||||||
void lcd_set_autoscroll(bool autoscroll);
|
void lcd_set_backlight(bool backlight, bool no_lock = false);
|
||||||
|
|
||||||
// Set backlight brightness
|
/// @brief Create a custom character. You get 8 custom characters.
|
||||||
void lcd_set_backlight(uint8_t brightness);
|
/// 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);
|
||||||
|
|
||||||
// Create a custom character
|
/// @brief Print a string to the LCD at a given pos.
|
||||||
void lcd_create_char(uint8_t location, uint8_t charmap[]);
|
/// @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);
|
||||||
|
|
||||||
// Write a character to the LCD
|
/// @brief Enables or disables the header on the LCD.
|
||||||
void lcd_write(uint8_t value);
|
/// @param enable `true` to enable the header, `false` to disable.
|
||||||
|
void set_lcd_header_enabled(bool enable);
|
||||||
|
|
||||||
// Print a string to the LCD
|
/// @brief Returns weather or not the lcd_header is enabled.
|
||||||
void lcd_print(const char* str);
|
/// @return `true` if the header is enabled, `false` otherwise.
|
||||||
|
bool lcd_header_enabled();
|
||||||
|
|
||||||
// Print a string to the LCD at a given pos
|
/// @brief Prints the header in the LCD.
|
||||||
void lcd_print(uint8_t col, uint8_t row, const char* str);
|
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 */
|
#endif /* CHAR_LCD_H */
|
||||||
@ -128,7 +128,7 @@ static esp_err_t panel_ili9488_init(esp_lcd_panel_t *panel)
|
|||||||
// ORIGINAL
|
// ORIGINAL
|
||||||
lcd_init_cmd_t ili9488_init[] =
|
lcd_init_cmd_t ili9488_init[] =
|
||||||
{
|
{
|
||||||
#if CONFIG_USE_NEW_DISPLAY || 1
|
#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_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 },
|
{ ILI9488_NEGATIVE_GAMMA_CTL, { 0x00, 0x11, 0x0d, 0x01, 0x0f, 0x05, 0x39, 0x36, 0x51, 0x06, 0x0f, 0x0d, 0x33, 0x37, 0x0F }, 15 },
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -1,326 +0,0 @@
|
|||||||
#include "bottom_half.h"
|
|
||||||
#include "inputs.hpp"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
static uint8_t reverse_4_bits(uint8_t value) {
|
|
||||||
return static_cast<uint8_t>(((value & 0x1) << 3) |
|
|
||||||
((value & 0x2) << 1) |
|
|
||||||
((value & 0x4) >> 1) |
|
|
||||||
((value & 0x8) >> 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
static KeypadKey map_input_keypad_key(InputKeypadKey key) {
|
|
||||||
switch (key) {
|
|
||||||
case InputKeypadKey::K0: return KeypadKey::k0;
|
|
||||||
case InputKeypadKey::K1: return KeypadKey::k1;
|
|
||||||
case InputKeypadKey::K2: return KeypadKey::k2;
|
|
||||||
case InputKeypadKey::K3: return KeypadKey::k3;
|
|
||||||
case InputKeypadKey::K4: return KeypadKey::k4;
|
|
||||||
case InputKeypadKey::K5: return KeypadKey::k5;
|
|
||||||
case InputKeypadKey::K6: return KeypadKey::k6;
|
|
||||||
case InputKeypadKey::K7: return KeypadKey::k7;
|
|
||||||
case InputKeypadKey::K8: return KeypadKey::k8;
|
|
||||||
case InputKeypadKey::K9: return KeypadKey::k9;
|
|
||||||
case InputKeypadKey::A: return KeypadKey::ka;
|
|
||||||
case InputKeypadKey::B: return KeypadKey::kb;
|
|
||||||
case InputKeypadKey::C: return KeypadKey::kc;
|
|
||||||
case InputKeypadKey::D: return KeypadKey::kd;
|
|
||||||
case InputKeypadKey::STAR: return KeypadKey::star;
|
|
||||||
case InputKeypadKey::POUND: return KeypadKey::pound;
|
|
||||||
default: return KeypadKey::k0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t get_fingerprint_touch_state() {
|
|
||||||
InputsState current = InputsController::get_input_state();
|
|
||||||
return static_cast<uint8_t>((current.touch_state >> 4) & 0x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool touch_state_initialized = false;
|
|
||||||
static bool touch_state_last = false;
|
|
||||||
|
|
||||||
static bool update_fingerprint_transition(bool want_pressed) {
|
|
||||||
bool current = get_fingerprint_touch_state();
|
|
||||||
if (!touch_state_initialized) {
|
|
||||||
touch_state_last = current;
|
|
||||||
touch_state_initialized = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool transition = want_pressed ? (current && !touch_state_last)
|
|
||||||
: (!current && touch_state_last);
|
|
||||||
touch_state_last = current;
|
|
||||||
return transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::array<SwitchFlip, 8> pending_switch_flips;
|
|
||||||
static size_t pending_switch_flip_count = 0;
|
|
||||||
|
|
||||||
static void push_pending_switch_flip(const SwitchFlip& event) {
|
|
||||||
if (pending_switch_flip_count < pending_switch_flips.size()) {
|
|
||||||
pending_switch_flips[pending_switch_flip_count++] = event;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the oldest event if the buffer is full.
|
|
||||||
for (size_t i = 1; i < pending_switch_flips.size(); ++i) {
|
|
||||||
pending_switch_flips[i - 1] = pending_switch_flips[i];
|
|
||||||
}
|
|
||||||
pending_switch_flips.back() = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pop_pending_switch_flip(bool want_up, SwitchFlip& out) {
|
|
||||||
for (size_t i = 0; i < pending_switch_flip_count; ++i) {
|
|
||||||
if (pending_switch_flips[i].is_up() == want_up) {
|
|
||||||
out = pending_switch_flips[i];
|
|
||||||
for (size_t j = i + 1; j < pending_switch_flip_count; ++j) {
|
|
||||||
pending_switch_flips[j - 1] = pending_switch_flips[j];
|
|
||||||
}
|
|
||||||
--pending_switch_flip_count;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clear_pending_switch_flips() {
|
|
||||||
pending_switch_flip_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::array<SwitchTouch, 8> pending_switch_touches;
|
|
||||||
static size_t pending_switch_touch_count = 0;
|
|
||||||
|
|
||||||
static void push_pending_switch_touch(const SwitchTouch& event) {
|
|
||||||
if (pending_switch_touch_count < pending_switch_touches.size()) {
|
|
||||||
pending_switch_touches[pending_switch_touch_count++] = event;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 1; i < pending_switch_touches.size(); ++i) {
|
|
||||||
pending_switch_touches[i - 1] = pending_switch_touches[i];
|
|
||||||
}
|
|
||||||
pending_switch_touches.back() = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pop_pending_switch_touch(bool want_touched, SwitchTouch& out) {
|
|
||||||
for (size_t i = 0; i < pending_switch_touch_count; ++i) {
|
|
||||||
if (pending_switch_touches[i].is_touched() == want_touched) {
|
|
||||||
out = pending_switch_touches[i];
|
|
||||||
for (size_t j = i + 1; j < pending_switch_touch_count; ++j) {
|
|
||||||
pending_switch_touches[j - 1] = pending_switch_touches[j];
|
|
||||||
}
|
|
||||||
--pending_switch_touch_count;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clear_pending_switch_touches() {
|
|
||||||
pending_switch_touch_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_bottom_half() {
|
|
||||||
init_expander();
|
|
||||||
clear_all_pressed_released();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_all_pressed_released() {
|
|
||||||
InputsController::clear_all_events();
|
|
||||||
clear_pending_switch_flips();
|
|
||||||
clear_pending_switch_touches();
|
|
||||||
touch_state_initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_keypad_pressed(KeypadKey* kp) {
|
|
||||||
auto opt = InputsController::get_keypad_press();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kp != nullptr) {
|
|
||||||
*kp = map_input_keypad_key(opt.value());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_keypad_released(KeypadKey* kp) {
|
|
||||||
auto opt = InputsController::get_keypad_release();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kp != nullptr) {
|
|
||||||
*kp = map_input_keypad_key(opt.value());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, std::optional<Button> opt) {
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (button != nullptr) {
|
|
||||||
*button = static_cast<ButtonKey>(static_cast<uint8_t>(opt.value()));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_button_pressed(ButtonKey* button) {
|
|
||||||
return take_button(button, InputsController::get_button_press());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_button_released(ButtonKey* button) {
|
|
||||||
return take_button(button, InputsController::get_button_release());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_button_state() {
|
|
||||||
return reverse_4_bits(InputsController::button_state() & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool take_switch_key(SwitchKey* switch_, const SwitchFlip& event) {
|
|
||||||
if (switch_ != nullptr) {
|
|
||||||
*switch_ = static_cast<SwitchKey>(static_cast<uint8_t>(event.get_switch()));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_switch_flipped_up(SwitchKey* switch_) {
|
|
||||||
SwitchFlip event;
|
|
||||||
if (pop_pending_switch_flip(true, event)) {
|
|
||||||
return take_switch_key(switch_, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto opt = InputsController::get_switch_flip();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (opt->is_up()) {
|
|
||||||
return take_switch_key(switch_, *opt);
|
|
||||||
}
|
|
||||||
push_pending_switch_flip(*opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_switch_flipped_down(SwitchKey* switch_) {
|
|
||||||
SwitchFlip event;
|
|
||||||
if (pop_pending_switch_flip(false, event)) {
|
|
||||||
return take_switch_key(switch_, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto opt = InputsController::get_switch_flip();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!opt->is_up()) {
|
|
||||||
return take_switch_key(switch_, *opt);
|
|
||||||
}
|
|
||||||
push_pending_switch_flip(*opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_switch_flipped(SwitchKey* switch_) {
|
|
||||||
if (pending_switch_flip_count > 0) {
|
|
||||||
SwitchFlip event = pending_switch_flips[0];
|
|
||||||
for (size_t i = 1; i < pending_switch_flip_count; ++i) {
|
|
||||||
pending_switch_flips[i - 1] = pending_switch_flips[i];
|
|
||||||
}
|
|
||||||
--pending_switch_flip_count;
|
|
||||||
return take_switch_key(switch_, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto opt = InputsController::get_switch_flip();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return take_switch_key(switch_, *opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_switch_state() {
|
|
||||||
return reverse_4_bits(InputsController::switch_state() & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool take_switch_touch(SwitchKey* switch_, const SwitchTouch& event) {
|
|
||||||
if (switch_ != nullptr) {
|
|
||||||
*switch_ = static_cast<SwitchKey>(static_cast<uint8_t>(event.get_switch()));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_switch_touch_pressed(SwitchKey* switch_) {
|
|
||||||
SwitchTouch event;
|
|
||||||
if (pop_pending_switch_touch(true, event)) {
|
|
||||||
return take_switch_touch(switch_, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto opt = InputsController::get_switch_touch();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (opt->is_touched()) {
|
|
||||||
return take_switch_touch(switch_, *opt);
|
|
||||||
}
|
|
||||||
push_pending_switch_touch(*opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_switch_touch_released(SwitchKey* switch_) {
|
|
||||||
SwitchTouch event;
|
|
||||||
if (pop_pending_switch_touch(false, event)) {
|
|
||||||
return take_switch_touch(switch_, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto opt = InputsController::get_switch_touch();
|
|
||||||
if (!opt.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!opt->is_touched()) {
|
|
||||||
return take_switch_touch(switch_, *opt);
|
|
||||||
}
|
|
||||||
push_pending_switch_touch(*opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_switch_touch_state() {
|
|
||||||
return reverse_4_bits(InputsController::switch_touch_state() & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_touch_state() {
|
|
||||||
return get_fingerprint_touch_state() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_touch_pressed() {
|
|
||||||
return update_fingerprint_transition(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_touch_released() {
|
|
||||||
return update_fingerprint_transition(false);
|
|
||||||
}
|
|
||||||
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 */
|
||||||
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
199
main/drivers/hwdata.h
Normal file
199
main/drivers/hwdata.h
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#ifndef HWDATA_H
|
||||||
|
#define HWDATA_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "nvs_flash.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 */
|
||||||
35
main/drivers/i2c.cpp
Normal file
35
main/drivers/i2c.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "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 */
|
||||||
@ -12,6 +12,7 @@
|
|||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "i2c.h"
|
||||||
|
|
||||||
#define TAG "I2C_LCD_PCF8574"
|
#define TAG "I2C_LCD_PCF8574"
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows) {
|
|||||||
lcd->entrymode = 0x02;
|
lcd->entrymode = 0x02;
|
||||||
|
|
||||||
// The following are the reset sequence: Please see "Initialization instruction in the PCF8574 datasheet."
|
// 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_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
i2c_master_start(cmd);
|
i2c_master_start(cmd);
|
||||||
// We left-shift the device addres and add the read/write command
|
// We left-shift the device addres and add the read/write command
|
||||||
@ -95,6 +97,7 @@ void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows) {
|
|||||||
i2c_master_stop(cmd);
|
i2c_master_stop(cmd);
|
||||||
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
i2c_cmd_link_delete(cmd);
|
i2c_cmd_link_delete(cmd);
|
||||||
|
xSemaphoreGive(main_i2c_mutex);
|
||||||
|
|
||||||
// Instruction: function set = 0x20
|
// Instruction: function set = 0x20
|
||||||
lcd_send(lcd, 0x20 | (rows > 1 ? 0x08 : 0x00), false);
|
lcd_send(lcd, 0x20 | (rows > 1 ? 0x08 : 0x00), false);
|
||||||
@ -110,7 +113,7 @@ void lcd_clear(i2c_lcd_pcf8574_handle_t* lcd) {
|
|||||||
// Instruction: Clear display = 0x01
|
// Instruction: Clear display = 0x01
|
||||||
lcd_send(lcd, 0x01, false);
|
lcd_send(lcd, 0x01, false);
|
||||||
// Clearing the display takes a while: takes approx. 1.5ms
|
// Clearing the display takes a while: takes approx. 1.5ms
|
||||||
esp_rom_delay_us(1600);
|
esp_rom_delay_us(2000);
|
||||||
} // lcd_clear()
|
} // lcd_clear()
|
||||||
|
|
||||||
// Set the display to home
|
// Set the display to home
|
||||||
@ -118,7 +121,7 @@ void lcd_home(i2c_lcd_pcf8574_handle_t* lcd) {
|
|||||||
// Instruction: Return home = 0x02
|
// Instruction: Return home = 0x02
|
||||||
lcd_send(lcd, 0x02, false);
|
lcd_send(lcd, 0x02, false);
|
||||||
// Same as clearing the display: takes approx. 1.5ms
|
// Same as clearing the display: takes approx. 1.5ms
|
||||||
esp_rom_delay_us(1600);
|
esp_rom_delay_us(2000);
|
||||||
} // lcd_home()
|
} // lcd_home()
|
||||||
|
|
||||||
// Set the cursor to a new position.
|
// Set the cursor to a new position.
|
||||||
@ -230,7 +233,7 @@ void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd) {
|
|||||||
|
|
||||||
// Setting the backlight: It can only be turn on or off.
|
// 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
|
// Current backlight value is saved in the i2c_lcd_pcf8574_handle_t struct for further data transfers
|
||||||
void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness) {
|
void lcd_set_backlight_to(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness) {
|
||||||
// Place the backlight value in the lcd struct
|
// Place the backlight value in the lcd struct
|
||||||
lcd->backlight = brightness;
|
lcd->backlight = brightness;
|
||||||
// Send no data
|
// Send no data
|
||||||
@ -238,7 +241,7 @@ void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness) {
|
|||||||
} // lcd_set_backlight()
|
} // lcd_set_backlight()
|
||||||
|
|
||||||
// Custom character creation: allows us to create up to 8 custom characters in the CGRAM locations
|
// 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, uint8_t charmap[]) {
|
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]) {
|
||||||
location &= 0x7; // Only 8 locations are available
|
location &= 0x7; // Only 8 locations are available
|
||||||
// Set the CGRAM address
|
// Set the CGRAM address
|
||||||
lcd_send(lcd, 0x40 | (location << 3), false);
|
lcd_send(lcd, 0x40 | (location << 3), false);
|
||||||
@ -255,7 +258,12 @@ void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value) {
|
|||||||
// Print characters to the LCD: cursor set or clear instruction must preceded this instruction, or it will write on the current text.
|
// 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) {
|
void lcd_print(i2c_lcd_pcf8574_handle_t* lcd, const char* str) {
|
||||||
while (*str) {
|
while (*str) {
|
||||||
lcd_write(lcd, *str++);
|
if (*str == '\x08') {
|
||||||
|
lcd_write(lcd, '\x00');
|
||||||
|
str++;
|
||||||
|
} else {
|
||||||
|
lcd_write(lcd, *str++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // lcd_print()
|
} // lcd_print()
|
||||||
|
|
||||||
@ -293,15 +301,16 @@ void lcd_print_number(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row, u
|
|||||||
|
|
||||||
// Private functions: derived from the esp32 i2c_master driver
|
// 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) {
|
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_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
i2c_master_start(cmd);
|
i2c_master_start(cmd);
|
||||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
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 >> 4 & 0x0F), is_data, cmd);
|
||||||
lcd_write_nibble(lcd, (value & 0x0F), is_data, cmd);
|
lcd_write_nibble(lcd, (value & 0x0F), is_data, cmd);
|
||||||
i2c_master_stop(cmd);
|
i2c_master_stop(cmd);
|
||||||
esp_err_t ret = i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
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);
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
@ -346,7 +355,9 @@ static void lcd_write_i2c(i2c_lcd_pcf8574_handle_t* lcd, uint8_t data, bool is_d
|
|||||||
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true);
|
||||||
i2c_master_write_byte(cmd, data, true);
|
i2c_master_write_byte(cmd, data, true);
|
||||||
i2c_master_stop(cmd);
|
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);
|
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);
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
|
|||||||
@ -91,10 +91,10 @@ void lcd_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
|
|||||||
void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
|
void lcd_no_autoscroll(i2c_lcd_pcf8574_handle_t* lcd);
|
||||||
|
|
||||||
// Set backlight brightness
|
// Set backlight brightness
|
||||||
void lcd_set_backlight(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness);
|
void lcd_set_backlight_to(i2c_lcd_pcf8574_handle_t* lcd, uint8_t brightness);
|
||||||
|
|
||||||
// Create a custom character
|
// Create a custom character
|
||||||
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, uint8_t charmap[]);
|
void lcd_create_char(i2c_lcd_pcf8574_handle_t* lcd, uint8_t location, const uint8_t charmap[]);
|
||||||
|
|
||||||
// Write a character to the LCD
|
// Write a character to the LCD
|
||||||
void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value);
|
void lcd_write(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value);
|
||||||
|
|||||||
@ -1,475 +0,0 @@
|
|||||||
#include "inputs.hpp"
|
|
||||||
#include "pins.h"
|
|
||||||
#include "driver/i2c.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_err.h"
|
|
||||||
|
|
||||||
static const char *TAG = "INPUTS";
|
|
||||||
|
|
||||||
static TaskHandle_t expander_task_handle = NULL;
|
|
||||||
|
|
||||||
const static uint8_t REG_WHOAMI = 0x01;
|
|
||||||
const static uint8_t REG_SW_VERSION = 0x02;
|
|
||||||
const static uint8_t REG_EVENT_QUEUE_POP = 0x10;
|
|
||||||
const static uint8_t REG_EVENT_QUEUE_LEN = 0x11;
|
|
||||||
const static uint8_t REG_STATE_BUTTONS = 0x20;
|
|
||||||
const static uint8_t REG_STATE_SWITCHES = 0x21;
|
|
||||||
const static uint8_t REG_STATE_KEYPAD = 0x22;
|
|
||||||
const static uint8_t REG_STATE_TOUCH = 0x23;
|
|
||||||
const static uint8_t REG_STATE_RFID = 0x24;
|
|
||||||
const static uint8_t REG_STATE_HALL = 0x25;
|
|
||||||
const static uint8_t REG_STATE_CLOSE = 0x26;
|
|
||||||
const static uint8_t REG_RESET = 0x30;
|
|
||||||
const static uint8_t REG_HALL_SENSITIVITY = 0x31;
|
|
||||||
const static uint8_t REG_CLOSE_SENSITIVITY = 0x32;
|
|
||||||
const static uint8_t REG_SWITCH_TOUCH_EVENT = 0x33;
|
|
||||||
|
|
||||||
/// The global data for the expander peripheral.
|
|
||||||
class ExpanderPeripheral {
|
|
||||||
// TODO: change these to private
|
|
||||||
// or even make this class hidden
|
|
||||||
public:
|
|
||||||
SemaphoreHandle_t state_mutex;
|
|
||||||
InputsState state;
|
|
||||||
|
|
||||||
// channels
|
|
||||||
QueueHandle_t button_press_events;
|
|
||||||
QueueHandle_t button_release_events;
|
|
||||||
QueueHandle_t switch_flip_events;
|
|
||||||
QueueHandle_t switch_touch_events;
|
|
||||||
QueueHandle_t touch_events;
|
|
||||||
QueueHandle_t keypad_press_events;
|
|
||||||
QueueHandle_t keypad_release_events;
|
|
||||||
};
|
|
||||||
|
|
||||||
ExpanderPeripheral expander_peripheral_singleton;
|
|
||||||
|
|
||||||
// forward declarations
|
|
||||||
static void get_events();
|
|
||||||
static void handle_event(uint8_t event);
|
|
||||||
static void handle_button_switch_event(uint8_t event);
|
|
||||||
static void handle_keypad_event(uint8_t event);
|
|
||||||
static void handle_touch_event(uint8_t event);
|
|
||||||
static void handle_rfid_event(uint8_t event);
|
|
||||||
static void handle_close_hal_event(uint8_t event);
|
|
||||||
static void expander_task(void *arg);
|
|
||||||
|
|
||||||
// ISR handler
|
|
||||||
static void IRAM_ATTR expander_isr_handler(void *arg) {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
if (expander_task_handle != NULL) {
|
|
||||||
vTaskNotifyGiveFromISR(expander_task_handle, &xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
|
||||||
portYIELD_FROM_ISR();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_expander() {
|
|
||||||
ESP_LOGI(TAG, "Initializing expander...");
|
|
||||||
|
|
||||||
// legacy I2C driver: use the shared I2C_NUM_0 bus already configured elsewhere.
|
|
||||||
// TODO: replace all these ESP_ERROR_CHECK with proper error handling that doesn't just crash the program
|
|
||||||
|
|
||||||
// setup interrupt on PIN_EXPANDER_INT
|
|
||||||
gpio_config_t io_conf = {
|
|
||||||
.pin_bit_mask = (1ULL << PIN_EXPANDER_INT),
|
|
||||||
.mode = GPIO_MODE_INPUT,
|
|
||||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
||||||
.intr_type = GPIO_INTR_NEGEDGE
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
|
||||||
|
|
||||||
// Install ISR service (only call once in your program)
|
|
||||||
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
|
||||||
|
|
||||||
// Attach the ISR to the expander pin
|
|
||||||
ESP_ERROR_CHECK(gpio_isr_handler_add(PIN_EXPANDER_INT, expander_isr_handler, NULL));
|
|
||||||
|
|
||||||
// verify the expander connection status by reading the WHOAMI register
|
|
||||||
uint8_t read_buf[2] = {0};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(i2c_master_write_read_device(I2C_NUM_0, EXPANDER_I2C_ADDR, ®_WHOAMI, 1, read_buf, 1, pdMS_TO_TICKS(EXPANDER_TIMEOUT_MS)));
|
|
||||||
|
|
||||||
if (read_buf[0] != EXPANDER_WHOAMI_VALUE) {
|
|
||||||
ESP_LOGE(TAG, "WHOAMI mismatch, expected 0x%02X, got 0x%02X", EXPANDER_WHOAMI_VALUE, read_buf[0]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Expander WHOAMI check passed");
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(i2c_master_write_read_device(I2C_NUM_0, EXPANDER_I2C_ADDR, ®_SW_VERSION, 1, read_buf, 2, pdMS_TO_TICKS(EXPANDER_TIMEOUT_MS)));
|
|
||||||
|
|
||||||
// init the peripheral struct
|
|
||||||
expander_peripheral_singleton.state_mutex = xSemaphoreCreateMutex();
|
|
||||||
expander_peripheral_singleton.button_press_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button));
|
|
||||||
expander_peripheral_singleton.button_release_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button));
|
|
||||||
expander_peripheral_singleton.switch_flip_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchFlip));
|
|
||||||
expander_peripheral_singleton.switch_touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchTouch));
|
|
||||||
expander_peripheral_singleton.touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(TouchedReleased));
|
|
||||||
expander_peripheral_singleton.keypad_press_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(InputKeypadKey));
|
|
||||||
expander_peripheral_singleton.keypad_release_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(InputKeypadKey));
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Expander initialized! SW version: v%d.%d", read_buf[0], read_buf[1]);
|
|
||||||
|
|
||||||
// Create the expander background worker task
|
|
||||||
BaseType_t task_created = xTaskCreate(
|
|
||||||
expander_task,
|
|
||||||
"expander_task",
|
|
||||||
4096,
|
|
||||||
NULL,
|
|
||||||
tskIDLE_PRIORITY + 1,
|
|
||||||
&expander_task_handle
|
|
||||||
);
|
|
||||||
|
|
||||||
if (task_created != pdPASS) {
|
|
||||||
ESP_LOGE(TAG, "Failed to create expander task");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void expander_task(void *arg) {
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
get_events();
|
|
||||||
|
|
||||||
// Wait for interrupt notification (signal is sent when INT falls)
|
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_events() {
|
|
||||||
uint8_t recv;
|
|
||||||
while (gpio_get_level(PIN_EXPANDER_INT) == 0) {
|
|
||||||
ESP_ERROR_CHECK(i2c_master_write_read_device(I2C_NUM_0, EXPANDER_I2C_ADDR, ®_EVENT_QUEUE_POP, 1, &recv, 1, pdMS_TO_TICKS(EXPANDER_TIMEOUT_MS)));
|
|
||||||
handle_event(recv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_event(uint8_t event) {
|
|
||||||
const uint8_t BUTTON_SWITCH = 0b000;
|
|
||||||
const uint8_t KEYPAD = 0b001;
|
|
||||||
const uint8_t TOUCH = 0b010;
|
|
||||||
const uint8_t RFID = 0b011;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Expander event: 0b%08b (0x%02X)", event, event);
|
|
||||||
|
|
||||||
if (event == 0) {
|
|
||||||
ESP_LOGE(TAG, "We read from event queue while it was empty!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t type_bits = event >> 5;
|
|
||||||
|
|
||||||
switch (type_bits) {
|
|
||||||
case BUTTON_SWITCH:
|
|
||||||
handle_button_switch_event(event);
|
|
||||||
break;
|
|
||||||
case KEYPAD:
|
|
||||||
handle_keypad_event(event);
|
|
||||||
break;
|
|
||||||
case TOUCH:
|
|
||||||
handle_touch_event(event);
|
|
||||||
break;
|
|
||||||
case RFID:
|
|
||||||
handle_rfid_event(event);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
handle_close_hal_event(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_button_switch_event(uint8_t event) {
|
|
||||||
const uint8_t PRESSED_NOT_RELEASED_BIT = 0b10000;
|
|
||||||
const uint8_t SWITCH_NOT_BUTTON_BIT = 0b01000;
|
|
||||||
const uint8_t SWITCH_UP_NOT_DOWN_BIT = 0b00100;
|
|
||||||
const uint8_t NUMBER_MASK = 0b00011;
|
|
||||||
|
|
||||||
bool pressed = (event & PRESSED_NOT_RELEASED_BIT) != 0;
|
|
||||||
uint8_t number = event & NUMBER_MASK;
|
|
||||||
|
|
||||||
if ((event & SWITCH_NOT_BUTTON_BIT) != 0) {
|
|
||||||
// For now, we support two position switches by only looking at the switch up events
|
|
||||||
bool switch_up = (event & SWITCH_UP_NOT_DOWN_BIT) != 0;
|
|
||||||
if (!switch_up) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch sw = static_cast<Switch>(number);
|
|
||||||
SwitchFlip sw_flip = SwitchFlip(sw, pressed);
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.switch_flip_events, &sw_flip, 0);
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
if (pressed) {
|
|
||||||
// set
|
|
||||||
expander_peripheral_singleton.state.switch_state |= 1 << number;
|
|
||||||
} else {
|
|
||||||
// clear
|
|
||||||
expander_peripheral_singleton.state.switch_state &= ~(1 << number);
|
|
||||||
}
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
} else {
|
|
||||||
// button
|
|
||||||
Button button = static_cast<Button>(number);
|
|
||||||
if (pressed) {
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.button_press_events, &button, 0);
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
expander_peripheral_singleton.state.button_state |= 1 << number;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
} else {
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.button_release_events, &button, 0);
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
expander_peripheral_singleton.state.button_state &= ~(1 << number);
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_keypad_event(uint8_t event) {
|
|
||||||
const uint8_t PRESSED_NOT_RELEASED_BIT = 0b10000;
|
|
||||||
const uint8_t KEY_MASK = 0b1111;
|
|
||||||
|
|
||||||
bool pressed = (event & PRESSED_NOT_RELEASED_BIT) != 0;
|
|
||||||
uint8_t number = event & KEY_MASK;
|
|
||||||
InputKeypadKey key = static_cast<InputKeypadKey>(number);
|
|
||||||
|
|
||||||
// starcode system gets first dibs
|
|
||||||
// TODO: do starcode inbetweener
|
|
||||||
// if starcode_handle_keypad(key, pressed).await {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (pressed) {
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.keypad_press_events, &key, 0);
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
expander_peripheral_singleton.state.keypad_state |= 1 << number;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
} else {
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.keypad_release_events, &key, 0);
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
expander_peripheral_singleton.state.keypad_state &= ~(1 << number);
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_touch_event(uint8_t event) {
|
|
||||||
const uint8_t TOUCHED_NOT_UNTOUCHED_BIT = 0b10000;
|
|
||||||
const uint8_t SENSOR_MASK = 0b0111;
|
|
||||||
const uint8_t FINGERPRINT_BIT = 0b0100;
|
|
||||||
|
|
||||||
bool touched = (event & TOUCHED_NOT_UNTOUCHED_BIT) != 0;
|
|
||||||
uint8_t sensor = event & SENSOR_MASK;
|
|
||||||
|
|
||||||
if ((sensor & FINGERPRINT_BIT) != 0) {
|
|
||||||
TouchedReleased touch_state = static_cast<TouchedReleased>(touched);
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.touch_events, &touch_state, 0);
|
|
||||||
} else {
|
|
||||||
Switch sw = static_cast<Switch>(sensor);
|
|
||||||
SwitchTouch sw_touch = SwitchTouch(sw, touched);
|
|
||||||
xQueueSendToBack(expander_peripheral_singleton.switch_touch_events, &sw_touch, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
if (touched) {
|
|
||||||
expander_peripheral_singleton.state.touch_state |= 1 << sensor;
|
|
||||||
} else {
|
|
||||||
expander_peripheral_singleton.state.touch_state &= ~(1 << sensor);
|
|
||||||
}
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_rfid_event(uint8_t event) {
|
|
||||||
// TODO: impl
|
|
||||||
(void)event;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_close_hal_event(uint8_t event) {
|
|
||||||
// TODO: impl
|
|
||||||
(void)event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputsController implementations
|
|
||||||
|
|
||||||
/// Clears all events waiting in the queues.
|
|
||||||
void InputsController::clear_all_events() {
|
|
||||||
xQueueReset(expander_peripheral_singleton.button_press_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.button_release_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.switch_flip_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.switch_touch_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.touch_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.keypad_press_events);
|
|
||||||
xQueueReset(expander_peripheral_singleton.keypad_release_events);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputsState InputsController::get_input_state() {
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
InputsState state_copy = expander_peripheral_singleton.state;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
return state_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a button press event waiting.
|
|
||||||
bool InputsController::has_button_press() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.button_press_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next button press event (if any).
|
|
||||||
std::optional<Button> InputsController::get_button_press() {
|
|
||||||
Button b;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.button_press_events, &b, 0) == pdTRUE) {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next button press event, waiting if neccesary.
|
|
||||||
Button InputsController::wait_button_press() {
|
|
||||||
Button b;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.button_press_events, &b, portMAX_DELAY);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current state of the buttons.
|
|
||||||
uint8_t InputsController::button_state() {
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
uint8_t value = expander_peripheral_singleton.state.button_state;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a button release event waiting.
|
|
||||||
bool InputsController::has_button_release() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.button_release_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next button release event (if any).
|
|
||||||
std::optional<Button> InputsController::get_button_release() {
|
|
||||||
Button b;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.button_release_events, &b, 0) == pdTRUE) {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next button release event, waiting if neccesary.
|
|
||||||
Button InputsController::wait_button_release() {
|
|
||||||
Button b;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.button_release_events, &b, portMAX_DELAY);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a switch flip event waiting.
|
|
||||||
bool InputsController::has_switch_flip() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.switch_flip_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next switch flip event (if any).
|
|
||||||
std::optional<SwitchFlip> InputsController::get_switch_flip() {
|
|
||||||
SwitchFlip s;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.switch_flip_events, &s, 0) == pdTRUE) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next switch flip event, waiting if neccesary.
|
|
||||||
SwitchFlip InputsController::wait_switch_flip() {
|
|
||||||
SwitchFlip s;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.switch_flip_events, &s, portMAX_DELAY);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current state of the switches.
|
|
||||||
uint8_t InputsController::switch_state() {
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
uint8_t value = expander_peripheral_singleton.state.switch_state;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a switch touch event waiting.
|
|
||||||
bool InputsController::has_switch_touch() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.switch_touch_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next switch touch event (if any).
|
|
||||||
std::optional<SwitchTouch> InputsController::get_switch_touch() {
|
|
||||||
SwitchTouch s;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.switch_touch_events, &s, 0) == pdTRUE) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next switch touch event, waiting if neccesary.
|
|
||||||
SwitchTouch InputsController::wait_switch_touch() {
|
|
||||||
SwitchTouch s;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.switch_touch_events, &s, portMAX_DELAY);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current state of the touch sensors.
|
|
||||||
uint8_t InputsController::switch_touch_state() {
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
uint8_t value = expander_peripheral_singleton.state.touch_state;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a keypad press event waiting.
|
|
||||||
bool InputsController::has_keypad_press() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.keypad_press_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next keypad press event (if any).
|
|
||||||
std::optional<InputKeypadKey> InputsController::get_keypad_press() {
|
|
||||||
InputKeypadKey k;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.keypad_press_events, &k, 0) == pdTRUE) {
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next keypad press event, waiting if neccesary.
|
|
||||||
InputKeypadKey InputsController::wait_keypad_press() {
|
|
||||||
InputKeypadKey k;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.keypad_press_events, &k, portMAX_DELAY);
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` iff there is a keypad release event waiting.
|
|
||||||
bool InputsController::has_keypad_release() {
|
|
||||||
return uxQueueMessagesWaiting(expander_peripheral_singleton.keypad_release_events) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next keypad release event (if any).
|
|
||||||
std::optional<InputKeypadKey> InputsController::get_keypad_release() {
|
|
||||||
InputKeypadKey k;
|
|
||||||
if (xQueueReceive(expander_peripheral_singleton.keypad_release_events, &k, 0) == pdTRUE) {
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the next keypad release event, waiting if neccesary.
|
|
||||||
InputKeypadKey InputsController::wait_keypad_release() {
|
|
||||||
InputKeypadKey k;
|
|
||||||
xQueueReceive(expander_peripheral_singleton.keypad_release_events, &k, portMAX_DELAY);
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current state of the keypad.
|
|
||||||
uint16_t InputsController::keypad_state() {
|
|
||||||
xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY);
|
|
||||||
uint16_t value = expander_peripheral_singleton.state.keypad_state;
|
|
||||||
xSemaphoreGive(expander_peripheral_singleton.state_mutex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,275 +0,0 @@
|
|||||||
#ifndef INPUTS_H
|
|
||||||
#define INPUTS_H
|
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
|
||||||
#include <freertos/queue.h>
|
|
||||||
#include <freertos/semphr.h>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#define EXPANDER_I2C_ADDR (0x7E)
|
|
||||||
#define EXPANDER_I2C_SPEED (400000)
|
|
||||||
// the actual transaction takes ~0.3ms, but for some reason a timout of ~10 or lower causes issues.
|
|
||||||
#define EXPANDER_TIMEOUT_MS (100)
|
|
||||||
|
|
||||||
#define EXPANDER_WHOAMI_VALUE (0x85)
|
|
||||||
|
|
||||||
// queue sizes
|
|
||||||
#define EXPANDER_EVENT_QUEUE_SIZE 4
|
|
||||||
#define EXPANDER_KEYPAD_QUEUE_SIZE 16
|
|
||||||
|
|
||||||
void init_expander();
|
|
||||||
|
|
||||||
/// The four buttons on the bottom half.
|
|
||||||
enum class Button: uint8_t {
|
|
||||||
B1 = 0,
|
|
||||||
B2 = 1,
|
|
||||||
B3 = 2,
|
|
||||||
B4 = 3,
|
|
||||||
GREEN = 0,
|
|
||||||
RED = 1,
|
|
||||||
YELLOW = 2,
|
|
||||||
BLUE = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint8_t raw_value(Button v) { return static_cast<uint8_t>(v); }
|
|
||||||
constexpr Button button_from_raw(uint8_t raw) { return static_cast<Button>(raw & 0b11); }
|
|
||||||
|
|
||||||
/// The four switches on the bottom half.
|
|
||||||
enum class Switch: uint8_t {
|
|
||||||
S1 = 0,
|
|
||||||
S2 = 1,
|
|
||||||
S3 = 2,
|
|
||||||
S4 = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint8_t raw_value(Switch v) { return static_cast<uint8_t>(v); }
|
|
||||||
constexpr Switch switch_from_raw(uint8_t raw) { return static_cast<Switch>(raw & 0b11); }
|
|
||||||
|
|
||||||
enum class TouchedReleased: uint8_t {
|
|
||||||
Released = 0,
|
|
||||||
Touched = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint8_t raw_value(TouchedReleased v) { return static_cast<uint8_t>(v); }
|
|
||||||
constexpr TouchedReleased touched_released_from_raw(uint8_t raw) { return static_cast<TouchedReleased>(raw & 0b1); }
|
|
||||||
|
|
||||||
/// One of the keys on the keypad.
|
|
||||||
enum class InputKeypadKey: uint8_t {
|
|
||||||
K0 = 0,
|
|
||||||
K1 = 1,
|
|
||||||
K2 = 2,
|
|
||||||
K3 = 3,
|
|
||||||
K4 = 4,
|
|
||||||
K5 = 5,
|
|
||||||
K6 = 6,
|
|
||||||
K7 = 7,
|
|
||||||
K8 = 8,
|
|
||||||
K9 = 9,
|
|
||||||
A = 10,
|
|
||||||
B = 11,
|
|
||||||
C = 12,
|
|
||||||
D = 13,
|
|
||||||
STAR = 14,
|
|
||||||
POUND = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint8_t raw_value(InputKeypadKey v) { return static_cast<uint8_t>(v); }
|
|
||||||
constexpr InputKeypadKey keypad_key_from_raw(uint8_t raw) { return static_cast<InputKeypadKey>(raw & 0b1111); }
|
|
||||||
|
|
||||||
constexpr char keypad_key_to_char(InputKeypadKey key) {
|
|
||||||
static constexpr char lookup[16] = {
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
||||||
'8', '9', 'A', 'B', 'C', 'D', '*', '#'
|
|
||||||
};
|
|
||||||
return lookup[static_cast<uint8_t>(key) & 0b1111];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct SwitchFlip {
|
|
||||||
private:
|
|
||||||
// [bit2: up] [bit1-0: switch]
|
|
||||||
uint8_t data;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
SwitchFlip(Switch sw, bool up)
|
|
||||||
: data((static_cast<uint8_t>(sw) & 0b11) |
|
|
||||||
((up ? 1 : 0) << 2)) {}
|
|
||||||
|
|
||||||
// Default constructor
|
|
||||||
SwitchFlip() : data(0) {}
|
|
||||||
|
|
||||||
// Raw value constructor
|
|
||||||
explicit SwitchFlip(uint8_t raw_data) : data(raw_data) {}
|
|
||||||
|
|
||||||
// Raw value getter
|
|
||||||
uint8_t raw() const { return data; }
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
Switch get_switch() const {
|
|
||||||
return static_cast<Switch>(data & 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_up() const {
|
|
||||||
return (data >> 2) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void set_switch(Switch sw) {
|
|
||||||
data = (data & ~0b11) | (static_cast<uint8_t>(sw) & 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_up(bool up) {
|
|
||||||
data = (data & ~(1 << 2)) | ((up ? 1 : 0) << 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SwitchFlip) == 1);
|
|
||||||
|
|
||||||
struct SwitchTouch {
|
|
||||||
private:
|
|
||||||
// [bit2: touched] [bit1-0: switch]
|
|
||||||
uint8_t data;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
SwitchTouch(Switch sw, bool touched)
|
|
||||||
: data((static_cast<uint8_t>(sw) & 0b11) |
|
|
||||||
((touched ? 1 : 0) << 2)) {}
|
|
||||||
|
|
||||||
// Default constructor
|
|
||||||
SwitchTouch() : data(0) {}
|
|
||||||
|
|
||||||
// Raw value constructor
|
|
||||||
explicit SwitchTouch(uint8_t raw_data) : data(raw_data) {}
|
|
||||||
|
|
||||||
// Raw value getter
|
|
||||||
uint8_t raw() const { return data; }
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
Switch get_switch() const {
|
|
||||||
return static_cast<Switch>(data & 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_touched() const {
|
|
||||||
return (data >> 2) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void set_switch(Switch sw) {
|
|
||||||
data = (data & ~0b11) | (static_cast<uint8_t>(sw) & 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_touched(bool touched) {
|
|
||||||
data = (data & ~(1 << 2)) | ((touched ? 1 : 0) << 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SwitchTouch) == 1);
|
|
||||||
|
|
||||||
struct ButtonOrSwitch {
|
|
||||||
private:
|
|
||||||
// [bit2: is_switch] [bit1-0: number]
|
|
||||||
uint8_t data;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
ButtonOrSwitch(uint8_t number, bool is_switch)
|
|
||||||
: data((number & 0b11) |
|
|
||||||
((is_switch ? 1 : 0) << 2)) {}
|
|
||||||
|
|
||||||
ButtonOrSwitch() : data(0) {}
|
|
||||||
|
|
||||||
// Raw value constructor
|
|
||||||
explicit ButtonOrSwitch(uint8_t raw_data) : data(raw_data) {}
|
|
||||||
|
|
||||||
// Raw value getter
|
|
||||||
uint8_t raw() const { return data; }
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
uint8_t number() const {
|
|
||||||
return data & 0b11;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_switch() const {
|
|
||||||
return (data >> 2) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void set_number(uint8_t number) {
|
|
||||||
data = (data & ~0b11) | (number & 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_is_switch(bool is_switch) {
|
|
||||||
data = (data & ~(1 << 2)) | ((is_switch ? 1 : 0) << 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static_assert(sizeof(ButtonOrSwitch) == 1);
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief The state of the bottom half of the box.
|
|
||||||
struct InputsState {
|
|
||||||
/// The touch state of the switches in the lower 4 bits.
|
|
||||||
/// The touch pad state in bit 4.
|
|
||||||
uint8_t touch_state;
|
|
||||||
/// The current state of the buttons in the lower 4 bits.
|
|
||||||
uint8_t button_state;
|
|
||||||
/// The current state of the switches. Up switches are stored
|
|
||||||
/// in the lower 4 bits, switches that are down are stored in
|
|
||||||
/// the upper 4 bits. If switches are in the middle, the
|
|
||||||
/// corresponding bit will be `0` in the upper and lower 4.
|
|
||||||
uint8_t switch_state;
|
|
||||||
/// The state of the keypad.
|
|
||||||
uint16_t keypad_state;
|
|
||||||
/// The sensitivity of the `hal` value to auto update.
|
|
||||||
uint16_t hal_sense;
|
|
||||||
/// The sensitivity of the `close_hal` value to auto update.
|
|
||||||
uint16_t close_hal_sense;
|
|
||||||
/// A non-exact hal value reading.
|
|
||||||
/// This only gets updated when it changes by `hal_sense`
|
|
||||||
uint16_t hal;
|
|
||||||
/// A non-exact hal value reading.
|
|
||||||
/// This only gets updated when it changes by `close_hal_sense`
|
|
||||||
uint16_t close_hal;
|
|
||||||
/// The RFID card that was presented last.
|
|
||||||
uint32_t rfid_state;
|
|
||||||
|
|
||||||
InputsState() : touch_state(0), button_state(0), switch_state(0), keypad_state(0), hal_sense(0), close_hal_sense(0), hal(0), close_hal(0), rfid_state(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class InputsController {
|
|
||||||
public:
|
|
||||||
static void clear_all_events();
|
|
||||||
|
|
||||||
static InputsState get_input_state();
|
|
||||||
|
|
||||||
static bool has_button_press();
|
|
||||||
static std::optional<Button> get_button_press();
|
|
||||||
static Button wait_button_press();
|
|
||||||
static uint8_t button_state();
|
|
||||||
|
|
||||||
static bool has_button_release();
|
|
||||||
static std::optional<Button> get_button_release();
|
|
||||||
static Button wait_button_release();
|
|
||||||
|
|
||||||
static bool has_switch_flip();
|
|
||||||
static std::optional<SwitchFlip> get_switch_flip();
|
|
||||||
static SwitchFlip wait_switch_flip();
|
|
||||||
static uint8_t switch_state();
|
|
||||||
|
|
||||||
static bool has_switch_touch();
|
|
||||||
static std::optional<SwitchTouch> get_switch_touch();
|
|
||||||
static SwitchTouch wait_switch_touch();
|
|
||||||
static uint8_t switch_touch_state();
|
|
||||||
|
|
||||||
static bool has_keypad_press();
|
|
||||||
static std::optional<InputKeypadKey> get_keypad_press();
|
|
||||||
static InputKeypadKey wait_keypad_press();
|
|
||||||
|
|
||||||
static bool has_keypad_release();
|
|
||||||
static std::optional<InputKeypadKey> get_keypad_release();
|
|
||||||
static InputKeypadKey wait_keypad_release();
|
|
||||||
static uint16_t keypad_state();
|
|
||||||
|
|
||||||
// TODO: impl and add the hal and RFID stuff
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // INPUTS_H
|
|
||||||
@ -8,11 +8,11 @@ static const char* TAG = "leds";
|
|||||||
|
|
||||||
static led_strip_handle_t leds;
|
static led_strip_handle_t leds;
|
||||||
|
|
||||||
|
// TODO: rename these to playback_handler
|
||||||
static bool replay_handler(const char* event, char* arg) {
|
static bool replay_handler(const char* event, char* arg) {
|
||||||
if (strcmp(event, "LED_SET") == 0) {
|
if (strcmp(event, "LED_SET") == 0) {
|
||||||
uint32_t led = atoi(strtok(arg, ","));
|
uint32_t led = atoi(strtok(arg, ","));
|
||||||
uint32_t color = atoi(strtok(NULL, ","));
|
uint32_t color = atoi(strtok(NULL, ","));
|
||||||
ESP_LOGI("leds", "color: %ld", color);
|
|
||||||
led_set(led, color);
|
led_set(led, color);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,10 @@
|
|||||||
#ifndef LEDS_H
|
#ifndef LEDS_H
|
||||||
#define LEDS_H
|
#define LEDS_H
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define LED_COUNT 21
|
#define LED_COUNT 21
|
||||||
|
|
||||||
#ifdef CONTROL_REV_2_0
|
|
||||||
#define NEOPIXEL_PIN GPIO_NUM_0
|
#define NEOPIXEL_PIN GPIO_NUM_0
|
||||||
#endif
|
|
||||||
#ifdef CONTROL_REV_2_1
|
|
||||||
#define NEOPIXEL_PIN GPIO_NUM_21
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONTROL_REV_2_0
|
|
||||||
#ifndef CONTROL_REV_2_1
|
|
||||||
#error "define rev2.0 or rev2.1"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||||
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||||
|
|
||||||
@ -39,6 +26,7 @@ enum LEDColor: uint32_t {
|
|||||||
LED_COLOR_WHITE_STRONG = 0xFF'FF'FF,
|
LED_COLOR_WHITE_STRONG = 0xFF'FF'FF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: sepperate the indicator leds from the shape display.
|
||||||
enum IndicatorLED {
|
enum IndicatorLED {
|
||||||
LED_SHAPE1 = 0u,
|
LED_SHAPE1 = 0u,
|
||||||
LED_SHAPE2 = 1u,
|
LED_SHAPE2 = 1u,
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
13
main/drivers/nvs.h
Normal file
13
main/drivers/nvs.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef NVS_H
|
||||||
|
#define NVS_H
|
||||||
|
|
||||||
|
#include "nvs_flash.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 */
|
||||||
@ -1,48 +0,0 @@
|
|||||||
#ifndef PINS_H
|
|
||||||
#define PINS_H
|
|
||||||
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
|
|
||||||
// ONLY INPUTS.HPP uses this file
|
|
||||||
|
|
||||||
#define PIN_SDA (GPIO_NUM_7)
|
|
||||||
#define PIN_SCL (GPIO_NUM_15)
|
|
||||||
|
|
||||||
#define PIN_LCD_MISO (GPIO_NUM_16)
|
|
||||||
#define PIN_LCD_MOSI (GPIO_NUM_17)
|
|
||||||
#define PIN_LCD_CLK (GPIO_NUM_18)
|
|
||||||
#define PIN_LCD_RS (GPIO_NUM_8)
|
|
||||||
#define PIN_LCD_RST (GPIO_NUM_9)
|
|
||||||
|
|
||||||
#define PIN_USB_DM (GPIO_NUM_19)
|
|
||||||
#define PIN_USB_DP (GPIO_NUM_20)
|
|
||||||
|
|
||||||
#define PIN_I2S_DAT (GPIO_NUM_3)
|
|
||||||
#define PIN_I2S_BCLK (GPIO_NUM_11)
|
|
||||||
#define PIN_I2S_LRCLK (GPIO_NUM_12)
|
|
||||||
|
|
||||||
#define PIN_SSEG_DAT (GPIO_NUM_46)
|
|
||||||
#define PIN_SSEG_CLK (GPIO_NUM_48)
|
|
||||||
|
|
||||||
#define PIN_MPU_INT (GPIO_NUM_10)
|
|
||||||
#define PIN_EXPANDER_INT (GPIO_NUM_13)
|
|
||||||
|
|
||||||
#define PIN_IR_RCV (GPIO_NUM_14)
|
|
||||||
|
|
||||||
// #define PIN_NEOPIXEL (GPIO_NUM_21) // Rev 2.1
|
|
||||||
#define PIN_NEOPIXEL (GPIO_NUM_0) // Rev 2.0
|
|
||||||
|
|
||||||
#define PIN_SD_DAT0 (GPIO_NUM_38)
|
|
||||||
#define PIN_SD_DAT1 (GPIO_NUM_47)
|
|
||||||
#define PIN_SD_DAT2 (GPIO_NUM_42)
|
|
||||||
#define PIN_SD_DAT3 (GPIO_NUM_41)
|
|
||||||
#define PIN_SD_CMD (GPIO_NUM_40)
|
|
||||||
#define PIN_SD_CLK (GPIO_NUM_39)
|
|
||||||
|
|
||||||
#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 // PINS_H
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include "char_lcd.h"
|
#include "char_lcd.h"
|
||||||
|
#include "starcode.h"
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
static const char* TAG = "power";
|
static const char* TAG = "power";
|
||||||
@ -11,9 +12,9 @@ void bat_monitor_task(void* arg) {
|
|||||||
sprintf(str_buf, "%d.%03dV", voltage / 1000, voltage % 1000);
|
sprintf(str_buf, "%d.%03dV", voltage / 1000, voltage % 1000);
|
||||||
|
|
||||||
lcd_clear();
|
lcd_clear();
|
||||||
lcd_print(1, 0, str_buf);
|
lcd_print(0, 1, str_buf);
|
||||||
|
|
||||||
int16_t current = lipo.current(current_measure::AVG);
|
int16_t current = lipo.current();
|
||||||
sprintf(str_buf, "%dmA", current);
|
sprintf(str_buf, "%dmA", current);
|
||||||
|
|
||||||
lcd_print(1, 1, str_buf);
|
lcd_print(1, 1, str_buf);
|
||||||
@ -22,12 +23,12 @@ void bat_monitor_task(void* arg) {
|
|||||||
int16_t total_cap = lipo.capacity(capacity_measure::FULL);
|
int16_t total_cap = lipo.capacity(capacity_measure::FULL);
|
||||||
sprintf(str_buf, "%dmAh", total_cap);
|
sprintf(str_buf, "%dmAh", total_cap);
|
||||||
|
|
||||||
lcd_print(1, 2, str_buf);
|
lcd_print(2, 1, str_buf);
|
||||||
|
|
||||||
int16_t soc = lipo.soc(soc_measure::FILTERED);
|
int16_t soc = lipo.soc(soc_measure::FILTERED);
|
||||||
sprintf(str_buf, "%d%%", soc);
|
sprintf(str_buf, "%d%%", soc);
|
||||||
|
|
||||||
lcd_print(1, 3, str_buf);
|
lcd_print(3, 1, str_buf);
|
||||||
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(250));
|
vTaskDelay(pdMS_TO_TICKS(250));
|
||||||
@ -50,4 +51,50 @@ void init_power_board() {
|
|||||||
ESP_LOGI(TAG, "Power board initialized!");
|
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);
|
||||||
|
// }
|
||||||
|
|||||||
@ -3,13 +3,18 @@
|
|||||||
|
|
||||||
#include "SparkFunBQ27441/SparkFunBQ27441.h"
|
#include "SparkFunBQ27441/SparkFunBQ27441.h"
|
||||||
|
|
||||||
|
extern volatile uint32_t battery_charge;
|
||||||
|
|
||||||
void bat_monitor_task(void* arg);
|
void bat_monitor_task(void* arg);
|
||||||
|
|
||||||
/// Initializes the battery gas guage for getting battery stats.
|
/// Initializes the battery gas guage for getting battery stats.
|
||||||
void init_power_board();
|
void init_power_board();
|
||||||
|
|
||||||
/// @brief Gets the battery voltage
|
/// @brief Gets the battery voltage.
|
||||||
/// @return battery voltage in mV.
|
/// @return battery voltage in mV.
|
||||||
uint16_t get_bat_voltage();
|
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 */
|
#endif /* POWER_H */
|
||||||
|
|||||||
@ -51,10 +51,10 @@ bool init_sd() {
|
|||||||
ESP_LOGE(TAG, "Failed to mount sd card: %s.", esp_err_to_name(ret));
|
ESP_LOGE(TAG, "Failed to mount sd card: %s.", esp_err_to_name(ret));
|
||||||
|
|
||||||
lcd_print(0, 0, "SD: ");
|
lcd_print(0, 0, "SD: ");
|
||||||
lcd_print(4, 0, esp_err_to_name(ret));
|
lcd_print(0, 4, esp_err_to_name(ret));
|
||||||
lcd_print(0, 1, "Press Green to retry");
|
lcd_print(1, 0, "Press Green to retry");
|
||||||
lcd_print(0, 2, "Press Yellow to skip");
|
lcd_print(2, 0, "Press Yellow to skip");
|
||||||
lcd_print(0, 3, "Press Red to format");
|
lcd_print(3, 0, "Press Red to format");
|
||||||
|
|
||||||
ButtonKey button;
|
ButtonKey button;
|
||||||
while (!( get_button_pressed(&button) && (button == ButtonKey::button_green || button == ButtonKey::button_red || button == ButtonKey::button_yellow) )) vTaskDelay(pdMS_TO_TICKS(10));
|
while (!( get_button_pressed(&button) && (button == ButtonKey::button_green || button == ButtonKey::button_red || button == ButtonKey::button_yellow) )) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#ifndef SD_H
|
#ifndef SD_H
|
||||||
#define SD_H
|
#define SD_H
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
#include <sys/unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -16,16 +15,10 @@ extern sdmmc_card_t *card;
|
|||||||
#define SD_PIN_CLK GPIO_NUM_39
|
#define SD_PIN_CLK GPIO_NUM_39
|
||||||
#define SD_PIN_CMD GPIO_NUM_40
|
#define SD_PIN_CMD GPIO_NUM_40
|
||||||
#define SD_PIN_D0 GPIO_NUM_38
|
#define SD_PIN_D0 GPIO_NUM_38
|
||||||
|
#define SD_PIN_D1 GPIO_NUM_45
|
||||||
#define SD_PIN_D2 GPIO_NUM_42
|
#define SD_PIN_D2 GPIO_NUM_42
|
||||||
#define SD_PIN_D3 GPIO_NUM_41
|
#define SD_PIN_D3 GPIO_NUM_41
|
||||||
|
|
||||||
#ifdef CONTROL_REV_2_0
|
|
||||||
#define SD_PIN_D1 GPIO_NUM_45
|
|
||||||
#endif
|
|
||||||
#ifdef CONTROL_REV_2_1
|
|
||||||
#define SD_PIN_D1 GPIO_NUM_47
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief Initializes the SD card
|
/// @brief Initializes the SD card
|
||||||
///
|
///
|
||||||
/// This requires the char_lcd to have been initialized.
|
/// This requires the char_lcd to have been initialized.
|
||||||
|
|||||||
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 */
|
||||||
@ -1,6 +1,7 @@
|
|||||||
#include "state_tracking.h"
|
#include "state_tracking.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "wlvgl.h"
|
||||||
|
|
||||||
static const char* PLAYBACK_TAG = "playback";
|
static const char* PLAYBACK_TAG = "playback";
|
||||||
|
|
||||||
@ -24,7 +25,6 @@ TaskHandle_t playback_task_handle;
|
|||||||
|
|
||||||
static volatile state_t state = STATE_IDLE;
|
static volatile state_t state = STATE_IDLE;
|
||||||
|
|
||||||
|
|
||||||
/// @brief Periodically flushes and syncs (if neccesary) the output stream.
|
/// @brief Periodically flushes and syncs (if neccesary) the output stream.
|
||||||
/// @param arg unused.
|
/// @param arg unused.
|
||||||
static void flush_file_task(void* arg) {
|
static void flush_file_task(void* arg) {
|
||||||
@ -171,6 +171,7 @@ bool start_recording() {
|
|||||||
state = STATE_RECORDING;
|
state = STATE_RECORDING;
|
||||||
recording_start_time = xTaskGetTickCount();
|
recording_start_time = xTaskGetTickCount();
|
||||||
xTaskCreate(flush_file_task, "flush_recording", 2048, NULL, 2, &flush_file_task_handle);
|
xTaskCreate(flush_file_task, "flush_recording", 2048, NULL, 2, &flush_file_task_handle);
|
||||||
|
reset_wlv_tables(); // TODO: generify this
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +199,7 @@ bool start_playback() {
|
|||||||
state = STATE_PLAYBACK;
|
state = STATE_PLAYBACK;
|
||||||
playback_start_time = xTaskGetTickCount();
|
playback_start_time = xTaskGetTickCount();
|
||||||
xTaskCreate(playback_task, "playback", 4096, NULL, 2, &playback_task_handle);
|
xTaskCreate(playback_task, "playback", 4096, NULL, 2, &playback_task_handle);
|
||||||
|
reset_wlv_tables(); // TODO: generify this
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ static bool notify_lvgl_flush_ready(
|
|||||||
esp_lcd_panel_io_event_data_t *edata,
|
esp_lcd_panel_io_event_data_t *edata,
|
||||||
void *user_ctx
|
void *user_ctx
|
||||||
) {
|
) {
|
||||||
lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
|
lv_disp_drv_t* disp_driver = (lv_disp_drv_t *)user_ctx;
|
||||||
lv_disp_flush_ready(disp_driver);
|
lv_disp_flush_ready(disp_driver);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
extern uint32_t current_step;
|
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 total_strikes;
|
||||||
uint32_t step_strikes[N_STEPS] = {0};
|
uint32_t step_strikes[N_STEPS] = {0};
|
||||||
uint32_t step_finish_times[N_STEPS] = {0};
|
uint32_t step_finish_times[N_STEPS] = {0};
|
||||||
@ -32,7 +35,7 @@ void init_wires(void) {
|
|||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.master = {
|
.master = {
|
||||||
.clk_speed = 100000,
|
.clk_speed = 100000,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio_reset_pin(PIN_WIRES_SDA);
|
gpio_reset_pin(PIN_WIRES_SDA);
|
||||||
@ -41,6 +44,10 @@ void init_wires(void) {
|
|||||||
ESP_ERROR_CHECK(i2c_param_config(WIRES_I2C_NUM, &wires_conf));
|
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));
|
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 = {};
|
gpio_config_t int_pin_conf = {};
|
||||||
// delta_pin_conf.intr_type = GPIO_INTR_LOW_LEVEL;
|
// delta_pin_conf.intr_type = GPIO_INTR_LOW_LEVEL;
|
||||||
int_pin_conf.mode = GPIO_MODE_INPUT;
|
int_pin_conf.mode = GPIO_MODE_INPUT;
|
||||||
@ -85,33 +92,41 @@ void clear_wires_pressed_released_cut(void) {
|
|||||||
|
|
||||||
void strike(const char* reason) {
|
void strike(const char* reason) {
|
||||||
ESP_LOGW("strike!", "%s", reason);
|
ESP_LOGW("strike!", "%s", reason);
|
||||||
lcd_print(0, 3, reason);
|
lcd_print(3, 0, reason);
|
||||||
time_penalty(STRIKE_TIME_PENALTY);
|
time_penalty(STRIKE_TIME_PENALTY);
|
||||||
if (current_step > 0 && current_step <= N_STEPS) {
|
if (current_step > 0 && current_step <= N_STEPS) {
|
||||||
total_strikes += 1;
|
total_strikes += 1;
|
||||||
step_strikes[current_step - 1] += 1;
|
step_strikes[current_step - 1] += 1;
|
||||||
}
|
}
|
||||||
uint8_t reg = 6;
|
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)));
|
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) {
|
void set_leds(uint8_t led_states) {
|
||||||
buf[0] = 5; // register 5
|
buf[0] = 5; // register 5
|
||||||
buf[1] = led_states;
|
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)));
|
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) {
|
static uint8_t receive_delta(void) {
|
||||||
uint8_t reg = 1;
|
uint8_t reg = 1;
|
||||||
buf[0] = 0;
|
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)));
|
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];
|
return buf[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void receive_wires(void) {
|
static void receive_wires(void) {
|
||||||
uint8_t reg = 2;
|
uint8_t reg = 2;
|
||||||
buf[0] = 0;
|
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)));
|
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 new_wires = buf[0];
|
||||||
|
|
||||||
uint8_t just_cut = ~new_wires & wires_state;
|
uint8_t just_cut = ~new_wires & wires_state;
|
||||||
@ -123,7 +138,9 @@ static void receive_wires(void) {
|
|||||||
static void receive_button(void) {
|
static void receive_button(void) {
|
||||||
uint8_t reg = 3;
|
uint8_t reg = 3;
|
||||||
buf[0] = 0;
|
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)));
|
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 new_button = buf[0] != 0;
|
||||||
|
|
||||||
bool just_pressed = new_button & !button_state;
|
bool just_pressed = new_button & !button_state;
|
||||||
|
|||||||
@ -5,16 +5,12 @@
|
|||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
#include "drivers/char_lcd.h"
|
#include "drivers/char_lcd.h"
|
||||||
#include "drivers/game_timer.h"
|
#include "drivers/game_timer.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "perh.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
|
|
||||||
|
|
||||||
#define WIRES_PIN_DELTA PIN_PERH3
|
#define WIRES_PIN_DELTA PIN_PERH3
|
||||||
#define PIN_WIRES_SDA PIN_PERH1
|
#define PIN_WIRES_SDA PIN_PERH1
|
||||||
@ -22,6 +18,9 @@
|
|||||||
#define WIRES_I2C_NUM I2C_NUM_1
|
#define WIRES_I2C_NUM I2C_NUM_1
|
||||||
#define WIRES_I2C_ADDR 125
|
#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_WIRES 0
|
||||||
#define DELTA_BIT_BUTTON 1
|
#define DELTA_BIT_BUTTON 1
|
||||||
|
|
||||||
|
|||||||
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 */
|
||||||
@ -17,76 +17,10 @@ void clean_bomb(void) {
|
|||||||
set_module_sseg_raw(clear);
|
set_module_sseg_raw(clear);
|
||||||
|
|
||||||
// clear char lcd
|
// clear char lcd
|
||||||
lcd_clear();
|
|
||||||
lcd_set_cursor_vis(false);
|
lcd_set_cursor_vis(false);
|
||||||
lcd_cursor_home();
|
lcd_clear();
|
||||||
}
|
|
||||||
|
|
||||||
static const int STRING_MAX_LEN = 8;
|
// TODO: add stuff for starcode system
|
||||||
void do_star_codes(StarCodeHandler* star_codes, int star_codes_len) {
|
|
||||||
KeypadKey key;
|
|
||||||
|
|
||||||
int current_idx = 0;
|
|
||||||
char current[STRING_MAX_LEN+1] = {0};
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
while (get_keypad_pressed(&key)) {
|
|
||||||
if (key == KeypadKey::star) {
|
|
||||||
current[0] = '*';
|
|
||||||
for (int i = 1; i < STRING_MAX_LEN; i++) {
|
|
||||||
current[i] = 0;
|
|
||||||
}
|
|
||||||
current_idx = 1;
|
|
||||||
} else if (key == KeypadKey::pound) {
|
|
||||||
// submit
|
|
||||||
if (current[0] == '\0') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hit = false;
|
|
||||||
for (int i = 0; i < star_codes_len; i++) {
|
|
||||||
StarCodeHandler sch = star_codes[i];
|
|
||||||
if (strcmp(current, sch.code) == 0) {
|
|
||||||
hit = true;
|
|
||||||
lcd_print(1, 2, sch.display_text);
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
||||||
if (sch.callback != nullptr) {
|
|
||||||
(sch.callback)();
|
|
||||||
}
|
|
||||||
if (sch.should_exit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hit) {
|
|
||||||
lcd_print(1, 2, "Invalid Star Code");
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear
|
|
||||||
for (int i = 0; i < STRING_MAX_LEN; i++) {
|
|
||||||
current[i] = 0;
|
|
||||||
}
|
|
||||||
current_idx = 0;
|
|
||||||
} else {
|
|
||||||
// out of room. skip
|
|
||||||
if (current_idx >= STRING_MAX_LEN) break;
|
|
||||||
// no code started.
|
|
||||||
if (current[0] != '*') continue;
|
|
||||||
|
|
||||||
char c = char_of_keypad_key(key);
|
|
||||||
current[current_idx++] = c;
|
|
||||||
}
|
|
||||||
// ESP_LOGI(STEP0_TAG, "Pressed: %c", c);
|
|
||||||
|
|
||||||
lcd_clear();
|
|
||||||
lcd_print(1, 1, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static lv_obj_t* old_scr;
|
static lv_obj_t* old_scr;
|
||||||
@ -120,7 +54,6 @@ void display_game_results(void) {
|
|||||||
|
|
||||||
lv_style_init(&game_results_style);
|
lv_style_init(&game_results_style);
|
||||||
lv_style_set_text_color(&game_results_style, lv_color_white());
|
lv_style_set_text_color(&game_results_style, lv_color_white());
|
||||||
// lv_style_set_bg_color(&game_results_style, lv_color_black());
|
|
||||||
lv_style_set_text_align(&game_results_style, LV_TEXT_ALIGN_LEFT);
|
lv_style_set_text_align(&game_results_style, LV_TEXT_ALIGN_LEFT);
|
||||||
|
|
||||||
overall_results_label = lv_label_create(scr);
|
overall_results_label = lv_label_create(scr);
|
||||||
|
|||||||
@ -10,20 +10,11 @@
|
|||||||
#include "drivers/speaker.h"
|
#include "drivers/speaker.h"
|
||||||
#include "drivers/tft.h"
|
#include "drivers/tft.h"
|
||||||
|
|
||||||
struct StarCodeHandler {
|
|
||||||
const char* code;
|
|
||||||
const char* display_text;
|
|
||||||
bool should_exit;
|
|
||||||
void (*callback)(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: add something for RNG.
|
// TODO: add something for RNG.
|
||||||
// TODO: add something for colors, to make everything consistant.
|
|
||||||
|
|
||||||
/// Clears most persistant bomb state
|
/// Clears most persistant bomb state
|
||||||
void clean_bomb(void);
|
void clean_bomb(void);
|
||||||
void poster_child_task(void* arg);
|
void poster_child_task(void* arg);
|
||||||
void do_star_codes(StarCodeHandler* star_codes, int star_codes_len);
|
|
||||||
void display_game_results();
|
void display_game_results();
|
||||||
|
|
||||||
#endif /* HELPER_H */
|
#endif /* HELPER_H */
|
||||||
|
|||||||
146
main/main.cpp
146
main/main.cpp
@ -13,104 +13,19 @@
|
|||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
|
||||||
#include "steps/step0.h"
|
#include "steps/step0.h"
|
||||||
#include "steps/p001_step1.h"
|
#include "steps/step1.h"
|
||||||
#include "steps/p001_step2.h"
|
#include "steps/step2.h"
|
||||||
#include "steps/p001_step3.h"
|
#include "steps/step3.h"
|
||||||
#include "steps/p001_step4.h"
|
#include "steps/step4.h"
|
||||||
#include "steps/p001_step5.h"
|
#include "steps/step5.h"
|
||||||
#include "steps/p001_step6.h"
|
#include "steps/step6.h"
|
||||||
#include "steps/p002_step1.h"
|
|
||||||
#include "steps/p002_step2.h"
|
|
||||||
#include "steps/p002_step3.h"
|
|
||||||
#include "steps/p002_step4.h"
|
|
||||||
#include "steps/p002_step5.h"
|
|
||||||
#include "steps/p002_step6.h"
|
|
||||||
|
|
||||||
bool play_modified;
|
|
||||||
|
|
||||||
static const char *TAG = "main";
|
static const char *TAG = "main";
|
||||||
uint32_t initial_game_time = 90*60*1000 + 1000;
|
uint32_t initial_game_time = 90*60*1000 + 1000;
|
||||||
uint32_t skip_to_step = 0;
|
uint32_t skip_to_step = 0;
|
||||||
uint32_t current_step = 0;
|
uint32_t current_step = 0;
|
||||||
uint32_t puzzle = 0;
|
|
||||||
extern uint32_t total_strikes;
|
extern uint32_t total_strikes;
|
||||||
|
|
||||||
static void do_p001() {
|
|
||||||
set_game_time(initial_game_time);
|
|
||||||
start_game_timer();
|
|
||||||
total_strikes = 0;
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 1;
|
|
||||||
if (skip_to_step <= 1) p001_step1();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 2;
|
|
||||||
if (skip_to_step <= 2) p001_step2();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 3;
|
|
||||||
if (skip_to_step <= 3) p001_step3();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 4;
|
|
||||||
if (skip_to_step <= 4) p001_step4();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 5;
|
|
||||||
if (skip_to_step <= 5) p001_step5();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 6;
|
|
||||||
if (skip_to_step <= 6) p001_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();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_p002() {
|
|
||||||
set_game_time(initial_game_time);
|
|
||||||
start_game_timer();
|
|
||||||
total_strikes = 0;
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 1;
|
|
||||||
if (skip_to_step <= 1) p002_step1();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 2;
|
|
||||||
if (skip_to_step <= 2) p002_step2();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 3;
|
|
||||||
if (skip_to_step <= 3) p002_step3();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 4;
|
|
||||||
if (skip_to_step <= 4) p002_step4();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 5;
|
|
||||||
if (skip_to_step <= 5) p002_step5();
|
|
||||||
step_finish_times[current_step-1] = get_game_time();
|
|
||||||
clean_bomb();
|
|
||||||
current_step = 6;
|
|
||||||
if (skip_to_step <= 6) p002_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();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void app_main(void) {
|
extern "C" void app_main(void) {
|
||||||
printf("app_main\n");
|
printf("app_main\n");
|
||||||
|
|
||||||
@ -122,11 +37,52 @@ extern "C" void app_main(void) {
|
|||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
clean_bomb();
|
clean_bomb();
|
||||||
|
lcd_do_splash();
|
||||||
step0();
|
step0();
|
||||||
|
|
||||||
if (puzzle == 1) {
|
// set_recording_source(stdout, false);
|
||||||
do_p001();
|
FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w");
|
||||||
} else {
|
if (record_file == nullptr) {
|
||||||
do_p002();
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
#ifndef MAIN_H
|
#ifndef MAIN_H
|
||||||
#define MAIN_H
|
#define MAIN_H
|
||||||
|
|
||||||
#define CONTROL_REV_2_0
|
|
||||||
// #define CONTROL_REV_2_1
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
extern bool play_modified;
|
|
||||||
|
|
||||||
constexpr size_t N_STEPS = 6;
|
constexpr size_t N_STEPS = 6;
|
||||||
|
|
||||||
#endif /* MAIN_H */
|
#endif /* MAIN_H */
|
||||||
@ -1,18 +1,12 @@
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
"setup_wires.cpp"
|
"setup_wires.cpp"
|
||||||
"step0.cpp"
|
"step0.cpp"
|
||||||
"p001_step1.cpp"
|
"step1.cpp"
|
||||||
"p001_step2.cpp"
|
"step2.cpp"
|
||||||
"p001_step3.cpp"
|
"step3.cpp"
|
||||||
"p001_step4.cpp"
|
"step4.cpp"
|
||||||
"p001_step5.cpp"
|
"step5.cpp"
|
||||||
"p001_step6.cpp"
|
"step6.cpp"
|
||||||
"p002_step1.cpp"
|
|
||||||
"p002_step2.cpp"
|
|
||||||
"p002_step3.cpp"
|
|
||||||
"p002_step4.cpp"
|
|
||||||
"p002_step5.cpp"
|
|
||||||
"p002_step6.cpp"
|
|
||||||
"wires_puzzle.cpp"
|
"wires_puzzle.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef P001_STEP_1_H
|
|
||||||
#define P001_STEP_1_H
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
|
|
||||||
void p001_step1(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_1_H */
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#ifndef P001_STEP_2_H
|
|
||||||
#define P001_STEP_2_H
|
|
||||||
|
|
||||||
#include "../drivers/bottom_half.h"
|
|
||||||
#include "../drivers/wires.h"
|
|
||||||
#include "../drivers/game_timer.h"
|
|
||||||
#include "../drivers/leds.h"
|
|
||||||
#include "../drivers/speaker.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <random>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
void p001_step2(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_2_H */
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef P001_STEP_3_H
|
|
||||||
#define P001_STEP_3_H
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
|
|
||||||
void p001_step3(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_3_H */
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
#ifndef P001_STEP_4_H
|
|
||||||
#define P001_STEP_4_H
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include "../drivers/tft.h"
|
|
||||||
#include "../drivers/speaker.h"
|
|
||||||
#include "../drivers/bottom_half.h"
|
|
||||||
#include "../drivers/game_timer.h"
|
|
||||||
#include "../drivers/wires.h"
|
|
||||||
#include "../drivers/char_lcd.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
|
|
||||||
#define TETRIS_USE_FLASH_IMG
|
|
||||||
#define TETRIS_USE_FLASH_BG_IMG
|
|
||||||
|
|
||||||
void p001_step4(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_4_H */
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
#ifndef P001_STEP_5_H
|
|
||||||
#define P001_STEP_5_H
|
|
||||||
|
|
||||||
#include "../drivers/bottom_half.h"
|
|
||||||
#include "../drivers/game_timer.h"
|
|
||||||
#include "../drivers/char_lcd.h"
|
|
||||||
#include "../drivers/leds.h"
|
|
||||||
#include "../drivers/wires.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
#include <random>
|
|
||||||
#include <iostream>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
void p001_step5(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_5_H */
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef P001_STEP_6_H
|
|
||||||
#define P001_STEP_6_H
|
|
||||||
|
|
||||||
#include "wires_puzzle.h"
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
|
|
||||||
void p001_step6(void);
|
|
||||||
|
|
||||||
#endif /* P001_STEP_6_H */
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "p002_step1.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step1";
|
|
||||||
|
|
||||||
void p002_step1(void) {
|
|
||||||
// TODO: implement step 1
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_1_H
|
|
||||||
#define P002_STEP_1_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step1(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_1_H */
|
|
||||||
@ -1,272 +0,0 @@
|
|||||||
#include "p002_step2.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step2";
|
|
||||||
|
|
||||||
static const uint8_t BOARD_COUNT = 9;
|
|
||||||
static const uint8_t BOARD_SIZE = 6;
|
|
||||||
static const uint8_t PART_SHOTS[3] = {20, 15, 12};
|
|
||||||
static const char *PART_LABELS[3] = {"Alpha", "Bravo", "Charlie"};
|
|
||||||
static const char ROW_LABELS[BOARD_SIZE] = {'A', 'B', 'C', 'D', 'E', 'F'};
|
|
||||||
|
|
||||||
static std::random_device random_device;
|
|
||||||
static std::mt19937 random_engine(random_device());
|
|
||||||
static std::uniform_int_distribution<int> random_board(0, BOARD_COUNT - 1);
|
|
||||||
|
|
||||||
static const uint8_t ALL_BOARDS[BOARD_COUNT][BOARD_SIZE][BOARD_SIZE] = {
|
|
||||||
{
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{0,0,1,1,1,1},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,1,0,1,1,0},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,0,0,1,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,1,0,1,1,0},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,0,0,1,0,0},
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{1,1,1,1,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,0,1,1,1,1},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,1,0,0,0,0},
|
|
||||||
{0,0,1,0,0,0},
|
|
||||||
{1,0,1,0,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{1,0,0,0,0,0},
|
|
||||||
{1,0,0,0,0,0},
|
|
||||||
{1,0,0,0,0,1},
|
|
||||||
{1,0,0,0,0,1},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
{0,1,1,1,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{1,1,0,1,0,0},
|
|
||||||
{0,0,0,1,0,0},
|
|
||||||
{0,1,0,1,0,0},
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{0,1,1,1,1,0},
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,0,1,0,1,0},
|
|
||||||
{1,0,1,0,1,0},
|
|
||||||
{0,0,1,0,1,0},
|
|
||||||
{0,0,1,0,0,0},
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{1,1,0,0,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,0,0,0,0,1},
|
|
||||||
{1,1,0,0,0,0},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
{1,1,1,0,1,0},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,1,1,0,0,0},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
{0,0,0,0,1,0},
|
|
||||||
{0,0,1,0,1,0},
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{1,1,1,1,0,0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{0,0,0,0,0,0},
|
|
||||||
{1,0,0,0,0,0},
|
|
||||||
{0,1,0,0,1,1},
|
|
||||||
{0,1,0,1,0,0},
|
|
||||||
{0,1,0,1,0,0},
|
|
||||||
{0,1,0,1,0,0},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool row_from_button_state(uint8_t state, uint8_t *row) {
|
|
||||||
switch (state) {
|
|
||||||
case 0b1100: *row = 0; return true; // B1 + B2 -> A
|
|
||||||
case 0b1010: *row = 1; return true; // B1 + B3 -> B
|
|
||||||
case 0b1001: *row = 2; return true; // B1 + B4 -> C
|
|
||||||
case 0b0110: *row = 3; return true; // B2 + B3 -> D
|
|
||||||
case 0b0101: *row = 4; return true; // B2 + B4 -> E
|
|
||||||
case 0b0011: *row = 5; return true; // B3 + B4 -> F
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool column_from_switch_state(uint8_t state, uint8_t *col) {
|
|
||||||
uint8_t value = state & 0xF;
|
|
||||||
if (value >= 1 && value <= 6) {
|
|
||||||
*col = value - 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count_ship_segments(const uint8_t board[BOARD_SIZE][BOARD_SIZE]) {
|
|
||||||
int count = 0;
|
|
||||||
for (uint8_t row = 0; row < BOARD_SIZE; ++row) {
|
|
||||||
for (uint8_t col = 0; col < BOARD_SIZE; ++col) {
|
|
||||||
if (board[row][col] == 1) {
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void copy_board(uint8_t dest[BOARD_SIZE][BOARD_SIZE], const uint8_t src[BOARD_SIZE][BOARD_SIZE]) {
|
|
||||||
memcpy(dest, src, BOARD_SIZE * BOARD_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_status(const char *part_name,
|
|
||||||
int shots_left,
|
|
||||||
int hits,
|
|
||||||
int misses,
|
|
||||||
int row,
|
|
||||||
int col,
|
|
||||||
const char *info) {
|
|
||||||
char line0[21] = {0};
|
|
||||||
char line1[21] = {0};
|
|
||||||
char line2[21] = {0};
|
|
||||||
char line3[21] = {0};
|
|
||||||
|
|
||||||
snprintf(line0, sizeof(line0), "%s Shots:%2d", part_name, shots_left);
|
|
||||||
if (row >= 0 && col >= 0) {
|
|
||||||
snprintf(line1, sizeof(line1), "Row %c Col %d", ROW_LABELS[row], col + 1);
|
|
||||||
} else {
|
|
||||||
snprintf(line1, sizeof(line1), "Row - Col -");
|
|
||||||
}
|
|
||||||
snprintf(line2, sizeof(line2), "Hits:%2d Misses:%2d", hits, misses);
|
|
||||||
snprintf(line3, sizeof(line3), "%s", info);
|
|
||||||
|
|
||||||
lcd_clear();
|
|
||||||
lcd_print(0, 0, line0);
|
|
||||||
lcd_print(0, 1, line1);
|
|
||||||
lcd_print(0, 2, line2);
|
|
||||||
lcd_print(0, 3, line3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void p002_step2(void) {
|
|
||||||
clean_bomb();
|
|
||||||
|
|
||||||
for (int part = 0; part < 3; ++part) {
|
|
||||||
const char *part_name = PART_LABELS[part];
|
|
||||||
const int max_shots = PART_SHOTS[part];
|
|
||||||
bool completed = false;
|
|
||||||
|
|
||||||
while (!completed) {
|
|
||||||
uint8_t board[BOARD_SIZE][BOARD_SIZE];
|
|
||||||
copy_board(board, ALL_BOARDS[random_board(random_engine)]);
|
|
||||||
|
|
||||||
int shots_taken = 0;
|
|
||||||
int hits = 0;
|
|
||||||
int misses = 0;
|
|
||||||
int current_row = -1;
|
|
||||||
int current_col = -1;
|
|
||||||
const char *status = "Hold 2 buttons + set switches";
|
|
||||||
|
|
||||||
render_status(part_name, max_shots - shots_taken, hits, misses, -1, -1, status);
|
|
||||||
clean_bomb();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
uint8_t button_state = get_button_state();
|
|
||||||
uint8_t switch_state = get_switch_state();
|
|
||||||
uint8_t row = 0;
|
|
||||||
uint8_t col = 0;
|
|
||||||
bool row_valid = row_from_button_state(button_state, &row);
|
|
||||||
bool col_valid = column_from_switch_state(switch_state, &col);
|
|
||||||
|
|
||||||
if (row_valid) {
|
|
||||||
current_row = row;
|
|
||||||
} else {
|
|
||||||
current_row = -1;
|
|
||||||
}
|
|
||||||
if (col_valid) {
|
|
||||||
current_col = col;
|
|
||||||
} else {
|
|
||||||
current_col = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeypadKey key;
|
|
||||||
if (get_keypad_pressed(&key)) {
|
|
||||||
if (key == KeypadKey::k5) {
|
|
||||||
if (!row_valid) {
|
|
||||||
status = "Invalid row selection";
|
|
||||||
} else if (!col_valid) {
|
|
||||||
status = "Invalid column value";
|
|
||||||
} else {
|
|
||||||
uint8_t cell = board[row][col];
|
|
||||||
if (cell == 2 || cell == 3) {
|
|
||||||
status = "Already fired there";
|
|
||||||
} else {
|
|
||||||
shots_taken += 1;
|
|
||||||
if (cell == 1) {
|
|
||||||
board[row][col] = 3;
|
|
||||||
hits += 1;
|
|
||||||
status = "Hit!";
|
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_RED);
|
|
||||||
} else {
|
|
||||||
board[row][col] = 2;
|
|
||||||
misses += 1;
|
|
||||||
status = "Miss!";
|
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_WHITE);
|
|
||||||
}
|
|
||||||
leds_flush();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(250));
|
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_OFF);
|
|
||||||
leds_flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_status(part_name,
|
|
||||||
max_shots - shots_taken,
|
|
||||||
hits,
|
|
||||||
misses,
|
|
||||||
current_row,
|
|
||||||
current_col,
|
|
||||||
status);
|
|
||||||
|
|
||||||
if (count_ship_segments(board) == 0) {
|
|
||||||
completed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shots_taken >= max_shots) {
|
|
||||||
strike("Out of shots!");
|
|
||||||
status = "Out of shots! Retry";
|
|
||||||
render_status(part_name,
|
|
||||||
0,
|
|
||||||
hits,
|
|
||||||
misses,
|
|
||||||
current_row,
|
|
||||||
current_col,
|
|
||||||
status);
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part < 2)
|
|
||||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
|
||||||
clean_bomb();
|
|
||||||
}
|
|
||||||
|
|
||||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
|
||||||
lcd_clear();
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_2_H
|
|
||||||
#define P002_STEP_2_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step2(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_2_H */
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "p002_step3.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step3";
|
|
||||||
|
|
||||||
void p002_step3(void) {
|
|
||||||
// TODO: implement step 3
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_3_H
|
|
||||||
#define P002_STEP_3_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step3(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_3_H */
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "p002_step4.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step4";
|
|
||||||
|
|
||||||
void p002_step4(void) {
|
|
||||||
// TODO: implement step 4
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_4_H
|
|
||||||
#define P002_STEP_4_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step4(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_4_H */
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "p002_step5.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step5";
|
|
||||||
|
|
||||||
void p002_step5(void) {
|
|
||||||
// TODO: implement step 5
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_5_H
|
|
||||||
#define P002_STEP_5_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step5(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_5_H */
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "p002_step6.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
|
||||||
static const char *TAG = "step6";
|
|
||||||
|
|
||||||
void p002_step6(void) {
|
|
||||||
// TODO: implement step 6
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef P002_STEP_6_H
|
|
||||||
#define P002_STEP_6_H
|
|
||||||
|
|
||||||
#include "../drivers/all.h"
|
|
||||||
|
|
||||||
void p002_step6(void);
|
|
||||||
|
|
||||||
#endif /* P002_STEP_6_H */
|
|
||||||
@ -11,7 +11,7 @@ void print_wires(WireColor* wires, int editing_idx) {
|
|||||||
lcd_print(1, 1, string_buf);
|
lcd_print(1, 1, string_buf);
|
||||||
|
|
||||||
cut_to_string(cut, string_buf);
|
cut_to_string(cut, string_buf);
|
||||||
lcd_print(1, 2, string_buf);
|
lcd_print(2, 1, string_buf);
|
||||||
|
|
||||||
wires_state = get_wires();
|
wires_state = get_wires();
|
||||||
for (int i = 0; i < NUM_WIRES; i++) {
|
for (int i = 0; i < NUM_WIRES; i++) {
|
||||||
@ -21,9 +21,10 @@ void print_wires(WireColor* wires, int editing_idx) {
|
|||||||
string_buf[i] = '!';
|
string_buf[i] = '!';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lcd_print(1, 3, string_buf);
|
lcd_print(3, 1, string_buf);
|
||||||
|
|
||||||
lcd_set_cursor_pos(editing_idx+1, 1);
|
lcd_set_cursor_vis(true);
|
||||||
|
lcd_set_cursor_resting_position(1, editing_idx+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_wires(void) {
|
void setup_wires(void) {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ static const char* TAG = "step0";
|
|||||||
|
|
||||||
extern uint32_t initial_game_time;
|
extern uint32_t initial_game_time;
|
||||||
extern uint32_t skip_to_step;
|
extern uint32_t skip_to_step;
|
||||||
extern uint32_t puzzle;
|
|
||||||
|
|
||||||
static void set_game_time();
|
static void set_game_time();
|
||||||
static void skip_to_step1() { skip_to_step = 1; }
|
static void skip_to_step1() { skip_to_step = 1; }
|
||||||
@ -14,12 +13,12 @@ static void skip_to_step3() { skip_to_step = 3; }
|
|||||||
static void skip_to_step4() { skip_to_step = 4; }
|
static void skip_to_step4() { skip_to_step = 4; }
|
||||||
static void skip_to_step5() { skip_to_step = 5; }
|
static void skip_to_step5() { skip_to_step = 5; }
|
||||||
static void skip_to_step6() { skip_to_step = 6; }
|
static void skip_to_step6() { skip_to_step = 6; }
|
||||||
static void try_step1() { clean_bomb(); p001_step1(); }
|
static void try_step1() { clean_bomb(); step1(); }
|
||||||
static void try_step2() { clean_bomb(); p001_step2(); }
|
static void try_step2() { clean_bomb(); step2(); }
|
||||||
static void try_step3() { clean_bomb(); p001_step3(); }
|
static void try_step3() { clean_bomb(); step3(); }
|
||||||
static void try_step4() { clean_bomb(); p001_step4(); }
|
static void try_step4() { clean_bomb(); step4(); }
|
||||||
static void try_step5() { clean_bomb(); p001_step5(); }
|
static void try_step5() { clean_bomb(); step5(); }
|
||||||
static void try_step6() { clean_bomb(); p001_step6(); }
|
static void try_step6() { clean_bomb(); step6(); }
|
||||||
static void issue_strike() { strike("Strike Issued"); }
|
static void issue_strike() { strike("Strike Issued"); }
|
||||||
static void flashbang();
|
static void flashbang();
|
||||||
static void debug_switches();
|
static void debug_switches();
|
||||||
@ -46,163 +45,173 @@ static void replay_last() {
|
|||||||
start_playback();
|
start_playback();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_p001() {
|
|
||||||
play_modified = false;
|
|
||||||
puzzle = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_p001_modified() {
|
|
||||||
play_modified = true;
|
|
||||||
puzzle = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_p002() {
|
|
||||||
puzzle = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait for "*9819"
|
|
||||||
void step0() {
|
void step0() {
|
||||||
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
|
led_set(IndicatorLED::LED_SPEAKER, LEDColor::LED_COLOR_BLUE);
|
||||||
leds_flush();
|
leds_flush();
|
||||||
|
|
||||||
StarCodeHandler star_codes[] = {
|
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||||
|
if (continue_sem == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "could not create semaphore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StarCodeEntry star_codes[] = {
|
||||||
{
|
{
|
||||||
.code = "*9819",
|
.code = "9819",
|
||||||
.display_text = "Start P001DH",
|
.display_text = "Diffusal Initiated",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = do_p001,
|
.callback = nullptr,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*9818",
|
.code = "59860",
|
||||||
.display_text = "Start P001DM",
|
.display_text = "Hardware Config",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = do_p001_modified,
|
.callback = hardware_config,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*3141",
|
.code = "59861",
|
||||||
.display_text = "Start P002",
|
|
||||||
.should_exit = true,
|
|
||||||
.callback = do_p002,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.code = "*59861",
|
|
||||||
.display_text = "Setup Wires",
|
.display_text = "Setup Wires",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = setup_wires,
|
.callback = setup_wires,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59862",
|
.code = "59862",
|
||||||
.display_text = "Set Game Time",
|
.display_text = "Set Game Time",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = set_game_time,
|
.callback = set_game_time,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59863",
|
.code = "59863",
|
||||||
.display_text = "Debug Switches",
|
.display_text = "Debug switches",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = debug_switches,
|
.callback = debug_switches,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59864",
|
.code = "59864",
|
||||||
.display_text = "Battery Stats",
|
.display_text = "Battery Stats",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = battery_stats,
|
.callback = battery_stats,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59871",
|
.code = "59871",
|
||||||
.display_text = "Try Step 1",
|
.display_text = "Try Step 1",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step1,
|
.callback = try_step1,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59872",
|
.code = "59872",
|
||||||
.display_text = "Try Step 2",
|
.display_text = "Try Step 2",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step2,
|
.callback = try_step2,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59873",
|
.code = "59873",
|
||||||
.display_text = "Try Step 3",
|
.display_text = "Try Step 3",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step3,
|
.callback = try_step3,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59874",
|
.code = "59874",
|
||||||
.display_text = "Try Step 4",
|
.display_text = "Try Step 4",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step4,
|
.callback = try_step4,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59875",
|
.code = "59875",
|
||||||
.display_text = "Try Step 5",
|
.display_text = "Try Step 5",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step5,
|
.callback = try_step5,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59876",
|
.code = "59876",
|
||||||
.display_text = "Try Step 6",
|
.display_text = "Try Step 6",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = try_step6,
|
.callback = try_step6,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59881",
|
.code = "59881",
|
||||||
.display_text = "Skip To Step 1",
|
.display_text = "Skip To Step 1",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step1,
|
.callback = skip_to_step1,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59882",
|
.code = "59882",
|
||||||
.display_text = "Skip To Step 2",
|
.display_text = "Skip To Step 2",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step2,
|
.callback = skip_to_step2,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59883",
|
.code = "59883",
|
||||||
.display_text = "Skip To Step 3",
|
.display_text = "Skip To Step 3",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step3,
|
.callback = skip_to_step3,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59884",
|
.code = "59884",
|
||||||
.display_text = "Skip To Step 4",
|
.display_text = "Skip To Step 4",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step4,
|
.callback = skip_to_step4,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59885",
|
.code = "59885",
|
||||||
.display_text = "Skip To Step 5",
|
.display_text = "Skip To Step 5",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step5,
|
.callback = skip_to_step5,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*59886",
|
.code = "59886",
|
||||||
.display_text = "Skip To Step 6",
|
.display_text = "Skip To Step 6",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = skip_to_step6,
|
.callback = skip_to_step6,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*1111",
|
.code = "1111",
|
||||||
.display_text = "Issue Strike",
|
.display_text = "Issue Strike",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = issue_strike,
|
.callback = issue_strike,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*1112",
|
.code = "1112",
|
||||||
.display_text = "????",
|
.display_text = "????",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = flashbang,
|
.callback = flashbang,
|
||||||
|
.triggered_sem = nullptr,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.code = "*1113",
|
.code = "1113",
|
||||||
.display_text = "replay_last",
|
.display_text = "replay",
|
||||||
.should_exit = false,
|
.delay_us = 2'000'000,
|
||||||
.callback = replay_last,
|
.callback = replay_last,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
int len = sizeof(star_codes)/sizeof(StarCodeHandler);
|
size_t len = sizeof(star_codes)/sizeof(star_codes[0]);
|
||||||
do_star_codes(star_codes, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 const int CURSOR_POS_MAP[5] = {1, 3, 4, 6, 7};
|
||||||
static char str_buf[18] = {0};
|
static char str_buf[18] = {0};
|
||||||
@ -212,7 +221,8 @@ static void _update_display(uint8_t* digits, uint8_t cursor_pos) {
|
|||||||
lcd_print(1, 1, str_buf);
|
lcd_print(1, 1, str_buf);
|
||||||
cursor_pos = MAX(0, MIN(4, cursor_pos));
|
cursor_pos = MAX(0, MIN(4, cursor_pos));
|
||||||
int mapped_cursor_pos = CURSOR_POS_MAP[cursor_pos];
|
int mapped_cursor_pos = CURSOR_POS_MAP[cursor_pos];
|
||||||
lcd_set_cursor_pos(mapped_cursor_pos, 1);
|
|
||||||
|
lcd_set_cursor_resting_position(1, mapped_cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_game_time() {
|
static void set_game_time() {
|
||||||
@ -333,32 +343,32 @@ static void debug_switches() {
|
|||||||
while (1) {
|
while (1) {
|
||||||
if (get_button_pressed(&button)) {
|
if (get_button_pressed(&button)) {
|
||||||
sprintf(buf, "Button Pressed: %d ", button);
|
sprintf(buf, "Button Pressed: %d ", button);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
if (get_button_released(&button)) {
|
if (get_button_released(&button)) {
|
||||||
sprintf(buf, "Button Released: %d", button);
|
sprintf(buf, "Button Released: %d", button);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
if (get_switch_flipped_down(&switch_)) {
|
if (get_switch_flipped_down(&switch_)) {
|
||||||
sprintf(buf, "Switch Down: %d ", switch_);
|
sprintf(buf, "Switch Down: %d ", switch_);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
if (get_switch_flipped_up(&switch_)) {
|
if (get_switch_flipped_up(&switch_)) {
|
||||||
sprintf(buf, "Switch Up: %d ", switch_);
|
sprintf(buf, "Switch Up: %d ", switch_);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
if (get_switch_touch_pressed(&switch_)) {
|
if (get_switch_touch_pressed(&switch_)) {
|
||||||
sprintf(buf, "Switch Touch: %d ", switch_);
|
sprintf(buf, "Switch Touch: %d ", switch_);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
if (get_switch_touch_released(&switch_)) {
|
if (get_switch_touch_released(&switch_)) {
|
||||||
sprintf(buf, "Switch Un-touch: %d", switch_);
|
sprintf(buf, "Switch Un-touch: %d", switch_);
|
||||||
lcd_print(0, 3, buf);
|
lcd_print(3, 0, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +377,7 @@ static void debug_switches() {
|
|||||||
switch_touch_state = new_switch_touch_state;
|
switch_touch_state = new_switch_touch_state;
|
||||||
print_4bin_rev(bin_buf, switch_touch_state);
|
print_4bin_rev(bin_buf, switch_touch_state);
|
||||||
sprintf(buf, "t: %s", bin_buf);
|
sprintf(buf, "t: %s", bin_buf);
|
||||||
lcd_print(1, 0, buf);
|
lcd_print(0, 1, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +395,7 @@ static void debug_switches() {
|
|||||||
button_state = new_button_state;
|
button_state = new_button_state;
|
||||||
print_4bin_rev(bin_buf, button_state);
|
print_4bin_rev(bin_buf, button_state);
|
||||||
sprintf(buf, "b: %s", bin_buf);
|
sprintf(buf, "b: %s", bin_buf);
|
||||||
lcd_print(1, 2, buf);
|
lcd_print(2, 1, buf);
|
||||||
ESP_LOGI(TAG, "%s", buf);
|
ESP_LOGI(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,15 @@
|
|||||||
#ifndef STEP_0_H
|
#ifndef STEP_0_H
|
||||||
#define STEP_0_H
|
#define STEP_0_H
|
||||||
|
|
||||||
#include "../drivers/bottom_half.h"
|
#include "../drivers/all.h"
|
||||||
#include "../drivers/char_lcd.h"
|
|
||||||
#include "../drivers/wires.h"
|
|
||||||
#include "../drivers/power.h"
|
|
||||||
#include "setup_wires.h"
|
|
||||||
#include "../helper.h"
|
|
||||||
|
|
||||||
#include "p001_step1.h"
|
#include "setup_wires.h"
|
||||||
#include "p001_step2.h"
|
#include "step1.h"
|
||||||
#include "p001_step3.h"
|
#include "step2.h"
|
||||||
#include "p001_step4.h"
|
#include "step3.h"
|
||||||
#include "p001_step5.h"
|
#include "step4.h"
|
||||||
#include "p001_step6.h"
|
#include "step5.h"
|
||||||
|
#include "step6.h"
|
||||||
|
|
||||||
/// Wait for "*9819"
|
/// Wait for "*9819"
|
||||||
void step0(void);
|
void step0(void);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#include "p001_step1.h"
|
#include "step1.h"
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static const char *TAG = "step1";
|
static const char *TAG = "step1";
|
||||||
@ -199,25 +199,24 @@ static int generate_part(void) {
|
|||||||
static void update_lcd_count(int times) {
|
static void update_lcd_count(int times) {
|
||||||
char buf[16] = {0};
|
char buf[16] = {0};
|
||||||
sprintf(buf, "%d/15", times);
|
sprintf(buf, "%d/15", times);
|
||||||
lcd_print(14, 1, buf);
|
lcd_print(1, 14, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool play_part(uint32_t time) {
|
static bool play_part(uint32_t time) {
|
||||||
set_module_time(time);
|
set_module_time(time);
|
||||||
|
|
||||||
lcd_clear();
|
lcd_clear();
|
||||||
lcd_set_cursor_pos(1, 1);
|
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case 0:
|
case 0:
|
||||||
lcd_print("COLOR");
|
lcd_print(1, 1, "COLOR");
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_PINK);
|
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_PINK);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
lcd_print("NUMBER");
|
lcd_print(1, 1, "NUMBER");
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_BLUE);
|
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_BLUE);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
lcd_print("SWITCH");
|
lcd_print(1, 1, "SWITCH");
|
||||||
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_YELLOW);
|
led_set(IndicatorLED::LED_LCD, LEDColor::LED_COLOR_YELLOW);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -276,7 +275,7 @@ static bool play_part(uint32_t time) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void p001_step1(void) {
|
void step1(void) {
|
||||||
while (get_switch_flipped(nullptr));
|
while (get_switch_flipped(nullptr));
|
||||||
|
|
||||||
init_step();
|
init_step();
|
||||||
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 */
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include "p001_step2.h"
|
#include "step2.h"
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static const char *TAG = "step2";
|
static const char *TAG = "step2";
|
||||||
@ -91,7 +91,7 @@ static void new_puzzle(void) {
|
|||||||
set_module_sseg_raw(display_map);
|
set_module_sseg_raw(display_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void p001_step2(void) {
|
void step2(void) {
|
||||||
KeypadKey key;
|
KeypadKey key;
|
||||||
int solved_times = 0;
|
int solved_times = 0;
|
||||||
|
|
||||||
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 */
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include "p001_step3.h"
|
#include "step3.h"
|
||||||
|
|
||||||
#define ONE_SECOND_TIME 90'000
|
#define ONE_SECOND_TIME 90'000
|
||||||
#define THREE_SECOND_TIME 90'000
|
#define THREE_SECOND_TIME 90'000
|
||||||
@ -21,6 +21,8 @@ static const char* TONE_FILES[] = {
|
|||||||
MOUNT_POINT "/high-6.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[] = {
|
static const char* LCD_STRINGS[] = {
|
||||||
"something",
|
"something",
|
||||||
"nothing",
|
"nothing",
|
||||||
@ -49,13 +51,20 @@ static std::uniform_int_distribution<> lcd_rand_char_dist(0, sizeof(lcd_random_c
|
|||||||
static std::uniform_int_distribution<> has_coconut_dist(0, 2);
|
static std::uniform_int_distribution<> has_coconut_dist(0, 2);
|
||||||
static std::uniform_int_distribution<> coconut_position_dist(0, 13);
|
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] = {
|
static uint32_t NEOPIXEL_COLORS[7] = {
|
||||||
LEDColor::LED_COLOR_RED,
|
LEDColor::LED_COLOR_RED,
|
||||||
LEDColor::LED_COLOR_ORANGE,
|
|
||||||
LEDColor::LED_COLOR_YELLOW,
|
LEDColor::LED_COLOR_YELLOW,
|
||||||
LEDColor::LED_COLOR_GREEN,
|
LEDColor::LED_COLOR_GREEN,
|
||||||
LEDColor::LED_COLOR_BLUE,
|
LEDColor::LED_COLOR_BLUE,
|
||||||
LEDColor::LED_COLOR_PINK,
|
LEDColor::LED_COLOR_PINK,
|
||||||
|
LEDColor::LED_COLOR_WHITE,
|
||||||
LEDColor::LED_COLOR_OFF,
|
LEDColor::LED_COLOR_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,17 +72,25 @@ static bool one_second();
|
|||||||
static bool three_second();
|
static bool three_second();
|
||||||
static bool six_second();
|
static bool six_second();
|
||||||
|
|
||||||
void p001_step3(void) {
|
void step3(void) {
|
||||||
StarCodeHandler star_codes[] = {
|
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||||
{
|
if (continue_sem == nullptr) {
|
||||||
.code = "*1642",
|
ESP_LOGE(TAG, "could not create semaphore");
|
||||||
.display_text = "Starting...",
|
return;
|
||||||
.should_exit = true,
|
}
|
||||||
.callback = nullptr,
|
|
||||||
},
|
StarCodeEntry start_code = {
|
||||||
|
.code = "1642",
|
||||||
|
.display_text = "Starting...",
|
||||||
|
.delay_us = 2'000'000,
|
||||||
|
.callback = nullptr,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
};
|
};
|
||||||
int len = sizeof(star_codes)/sizeof(StarCodeHandler);
|
|
||||||
do_star_codes(star_codes, len);
|
add_star_code(start_code);
|
||||||
|
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||||
|
rm_star_code(start_code.code);
|
||||||
|
vSemaphoreDelete(continue_sem);
|
||||||
|
|
||||||
while (times < TIMES_TO_COMPLETE) {
|
while (times < TIMES_TO_COMPLETE) {
|
||||||
tone = tone_dist(gen);
|
tone = tone_dist(gen);
|
||||||
@ -82,7 +99,7 @@ void p001_step3(void) {
|
|||||||
|
|
||||||
play_clip_wav(MOUNT_POINT "/ready.wav", true, false, 3, 0);
|
play_clip_wav(MOUNT_POINT "/ready.wav", true, false, 3, 0);
|
||||||
// The high pitched tones need to be scaled down by 3 more
|
// The high pitched tones need to be scaled down by 3 more
|
||||||
play_clip_wav(TONE_FILES[tone], false, false, 2 + (tone/3) * 3, 0);
|
play_clip_wav(TONE_FILES[tone], false, false, 1 + (tone/3) * 4, 0);
|
||||||
|
|
||||||
bool correct = false;
|
bool correct = false;
|
||||||
switch (tone % 3) {
|
switch (tone % 3) {
|
||||||
@ -221,16 +238,16 @@ static bool one_second() {
|
|||||||
int red_led_count = 0;
|
int red_led_count = 0;
|
||||||
int blue_led_count = 0;
|
int blue_led_count = 0;
|
||||||
for (int i = 0; i < LED_COUNT; i++) {
|
for (int i = 0; i < LED_COUNT; i++) {
|
||||||
if (indicator_led_idxs[i] == 0) {
|
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_RED) {
|
||||||
red_led_count++;
|
red_led_count++;
|
||||||
} else if (indicator_led_idxs[i] == 4) {
|
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||||
blue_led_count++;
|
blue_led_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t correct_switches = four_bit_flag(
|
uint8_t correct_switches = four_bit_flag(
|
||||||
speaker_color == 0 || speaker_color == 1 || speaker_color == 2,
|
speaker_color == NEOPIXEL_COLOR_IDX_RED || speaker_color == NEOPIXEL_COLOR_IDX_YELLOW || speaker_color == NEOPIXEL_COLOR_IDX_PINK,
|
||||||
lcd_string_idx == 0 || lcd_string_idx == 1,
|
lcd_string_idx == LCD_STRING_SOMETHING || lcd_string_idx == LCD_STRING_NOTHING,
|
||||||
was_high,
|
was_high,
|
||||||
!was_high
|
!was_high
|
||||||
);
|
);
|
||||||
@ -280,9 +297,9 @@ static bool three_second() {
|
|||||||
int red_led_count = 0;
|
int red_led_count = 0;
|
||||||
int blue_led_count = 0;
|
int blue_led_count = 0;
|
||||||
for (int i = 0; i < LED_COUNT; i++) {
|
for (int i = 0; i < LED_COUNT; i++) {
|
||||||
if (indicator_led_idxs[i] == 0) {
|
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_RED) {
|
||||||
red_led_count++;
|
red_led_count++;
|
||||||
} else if (indicator_led_idxs[i] == 4) {
|
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||||
blue_led_count++;
|
blue_led_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,7 +362,7 @@ static bool six_second() {
|
|||||||
|
|
||||||
bool was_high = (tone / 3) == 1;
|
bool was_high = (tone / 3) == 1;
|
||||||
|
|
||||||
bool second_switch_correct_state = (indicator_led_idxs[IndicatorLED::LED_S2] == 0) || (indicator_led_idxs[IndicatorLED::LED_S2] == 6);
|
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;
|
second_switch_correct_state = second_switch_correct_state || was_high;
|
||||||
|
|
||||||
rng_leds();
|
rng_leds();
|
||||||
@ -354,16 +371,16 @@ static bool six_second() {
|
|||||||
int green_led_count = 0;
|
int green_led_count = 0;
|
||||||
int blue_led_count = 0;
|
int blue_led_count = 0;
|
||||||
for (int i = 0; i < LED_COUNT; i++) {
|
for (int i = 0; i < LED_COUNT; i++) {
|
||||||
if (indicator_led_idxs[i] == 4) {
|
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_BLUE) {
|
||||||
blue_led_count++;
|
blue_led_count++;
|
||||||
} else if (indicator_led_idxs[i] == 3) {
|
} else if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_GREEN) {
|
||||||
green_led_count++;
|
green_led_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pink_led_on_bottom_count = 0;
|
int pink_led_on_bottom_count = 0;
|
||||||
for (int i = IndicatorLED::LED_RFID; i < LED_COUNT; i++) {
|
for (int i = IndicatorLED::LED_RFID; i < LED_COUNT; i++) {
|
||||||
if (indicator_led_idxs[i] == 5) {
|
if (indicator_led_idxs[i] == NEOPIXEL_COLOR_IDX_PINK) {
|
||||||
pink_led_on_bottom_count++;
|
pink_led_on_bottom_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 */
|
||||||
@ -1,5 +1,4 @@
|
|||||||
#include "p001_step4.h"
|
#include "step4.h"
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static const char *TAG = "step4";
|
static const char *TAG = "step4";
|
||||||
@ -234,11 +233,8 @@ bool play_game(int time, int required_score) {
|
|||||||
target_score = required_score;
|
target_score = required_score;
|
||||||
score = 0;
|
score = 0;
|
||||||
update_score();
|
update_score();
|
||||||
|
set_module_time(time);
|
||||||
if (!play_modified) {
|
start_module_timer();
|
||||||
set_module_time(time);
|
|
||||||
start_module_timer();
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_board();
|
clear_board();
|
||||||
|
|
||||||
@ -276,8 +272,7 @@ bool play_game(int time, int required_score) {
|
|||||||
show_board();
|
show_board();
|
||||||
|
|
||||||
if (score >= required_score) {
|
if (score >= required_score) {
|
||||||
if (!play_modified)
|
stop_module_timer();
|
||||||
stop_module_timer();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,20 +307,18 @@ bool play_game(int time, int required_score) {
|
|||||||
show_board();
|
show_board();
|
||||||
|
|
||||||
if (score >= required_score) {
|
if (score >= required_score) {
|
||||||
if (!play_modified)
|
stop_module_timer();
|
||||||
stop_module_timer();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!play_modified) && get_module_time() <= 0) {
|
if (get_module_time() <= 0) {
|
||||||
stop_module_timer();
|
stop_module_timer();
|
||||||
strike("Out of time");
|
strike("Out of time");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (get_switch_flipped(&switch_)) {
|
// if (get_switch_flipped(&switch_)) {
|
||||||
// printf("%d\n", piece);
|
// printf("%d\n", piece);
|
||||||
// for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
// for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
|
||||||
@ -340,8 +333,7 @@ bool play_game(int time, int required_score) {
|
|||||||
|
|
||||||
// game over
|
// game over
|
||||||
ESP_LOGI(TAG, "Game Over. Score: %d", score);
|
ESP_LOGI(TAG, "Game Over. Score: %d", score);
|
||||||
if (!play_modified)
|
stop_module_timer();
|
||||||
stop_module_timer();
|
|
||||||
strike("Out of room");
|
strike("Out of room");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -387,26 +379,36 @@ static void fail() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void p001_step4() {
|
void step4() {
|
||||||
StarCodeHandler star_code = {
|
// TODO: extract to helper function
|
||||||
.code = "*3850",
|
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||||
|
if (continue_sem == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "could not create semaphore");
|
||||||
|
}
|
||||||
|
|
||||||
|
StarCodeEntry start_code = {
|
||||||
|
.code = "3850",
|
||||||
.display_text = "Starting...",
|
.display_text = "Starting...",
|
||||||
.should_exit = true,
|
.delay_us = 2'000'000,
|
||||||
.callback = nullptr,
|
.callback = nullptr,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
};
|
};
|
||||||
do_star_codes(&star_code, 1);
|
|
||||||
|
add_star_code(start_code);
|
||||||
|
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||||
|
rm_star_code(start_code.code);
|
||||||
|
vSemaphoreDelete(continue_sem);
|
||||||
|
|
||||||
init_screen();
|
init_screen();
|
||||||
|
|
||||||
int lines_1 = play_modified ? 2 : 2;
|
while (!play_game(4*60*1000, 2)) fail();
|
||||||
int lines_2 = play_modified ? 5 : 4;
|
// TODO: create constants for common assets, and put them in a folder.
|
||||||
int lines_3 = play_modified ? 12 : 8;
|
|
||||||
while (!play_game(4*60*1000, lines_1)) fail();
|
|
||||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||||
complete();
|
complete();
|
||||||
while (!play_game(4*60*1000, lines_2)) fail();
|
while (!play_game(4*60*1000, 4)) fail();
|
||||||
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||||
complete();
|
complete();
|
||||||
while (!play_game(6*60*1000, lines_3)) fail();
|
while (!play_game(7*60*1000, 8)) fail();
|
||||||
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||||
complete();
|
complete();
|
||||||
|
|
||||||
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 */
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include "p001_step5.h"
|
#include "step5.h"
|
||||||
|
|
||||||
#define TIME_CLEAR 30'000
|
#define TIME_CLEAR 30'000
|
||||||
#define TIME_PLANK 40'000
|
#define TIME_PLANK 40'000
|
||||||
@ -178,17 +178,24 @@ bool submit_6(bool* buttons_cycling, bool button_turned_on, int led_off) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void p001_step5(void) {
|
void step5(void) {
|
||||||
StarCodeHandler star_codes[] = {
|
SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
|
||||||
{
|
if (continue_sem == nullptr) {
|
||||||
.code = "*2648",
|
ESP_LOGE(TAG, "could not create semaphore");
|
||||||
.display_text = "Starting...",
|
}
|
||||||
.should_exit = true,
|
|
||||||
.callback = nullptr,
|
StarCodeEntry start_code = {
|
||||||
},
|
.code = "2648",
|
||||||
|
.display_text = "Starting...",
|
||||||
|
.delay_us = 2'000'000,
|
||||||
|
.callback = nullptr,
|
||||||
|
.triggered_sem = continue_sem,
|
||||||
};
|
};
|
||||||
int len = sizeof(star_codes)/sizeof(StarCodeHandler);
|
|
||||||
do_star_codes(star_codes, len);
|
add_star_code(start_code);
|
||||||
|
xSemaphoreTake(continue_sem, portMAX_DELAY);
|
||||||
|
rm_star_code(start_code.code);
|
||||||
|
vSemaphoreDelete(continue_sem);
|
||||||
|
|
||||||
std::vector<int> all_leds;
|
std::vector<int> all_leds;
|
||||||
|
|
||||||
@ -222,15 +229,13 @@ void p001_step5(void) {
|
|||||||
clean_bomb();
|
clean_bomb();
|
||||||
int solved_puzzles = 0;
|
int solved_puzzles = 0;
|
||||||
while (solved_puzzles < TIMES_TO_SOLVE) {
|
while (solved_puzzles < TIMES_TO_SOLVE) {
|
||||||
lcd_set_cursor_pos(1, 1);
|
|
||||||
bool solved_correctly = false;
|
bool solved_correctly = false;
|
||||||
|
|
||||||
int puzzle = puzzle_dist(gen);
|
int puzzle = puzzle_dist(gen);
|
||||||
// int puzzle = 2;
|
|
||||||
|
|
||||||
switch (puzzle) {
|
switch (puzzle) {
|
||||||
case 0: {
|
case 0: {
|
||||||
lcd_print("Clear");
|
lcd_print(1, 1, "Clear");
|
||||||
set_module_time(TIME_CLEAR);
|
set_module_time(TIME_CLEAR);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -265,7 +270,7 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
lcd_print("Blank");
|
lcd_print(1, 1, "Blank");
|
||||||
set_module_time(TIME_BLANK);
|
set_module_time(TIME_BLANK);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -379,7 +384,7 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
lcd_print("Nothing");
|
lcd_print(1, 1, "Nothing");
|
||||||
set_module_time(TIME_NOTHING);
|
set_module_time(TIME_NOTHING);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -438,7 +443,7 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
lcd_print("Blink");
|
lcd_print(1, 1, "Blink");
|
||||||
set_module_time(TIME_BLINK);
|
set_module_time(TIME_BLINK);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -502,7 +507,7 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 5: {
|
case 5: {
|
||||||
lcd_print("Ummm");
|
lcd_print(1, 1, "Ummm");
|
||||||
set_module_time(TIME_UMMM);
|
set_module_time(TIME_UMMM);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -558,7 +563,7 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 6: {
|
case 6: {
|
||||||
lcd_print("Plank");
|
lcd_print(1, 1, "Plank");
|
||||||
set_module_time(TIME_PLANK);
|
set_module_time(TIME_PLANK);
|
||||||
start_module_timer();
|
start_module_timer();
|
||||||
|
|
||||||
@ -651,239 +656,231 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 7: {
|
case 7: {
|
||||||
if (play_modified) {
|
lcd_print(1, 1, "What");
|
||||||
// do PLINK
|
set_module_time(TIME_WHAT);
|
||||||
lcd_print("Plink");
|
start_module_timer();
|
||||||
set_module_time(TIME_PLINK);
|
|
||||||
start_module_timer();
|
|
||||||
|
|
||||||
std::uniform_int_distribution<> indicator_number_dist(0, 4);
|
std::uniform_int_distribution<> math_number_dist(1, 9);
|
||||||
|
|
||||||
// ESP_LOGI(TAG, "Green: %i, Red: %i, Yellow: %i, Blue: %i", green_indicators, red_indicators, yellow_indicators, blue_indicators);
|
std::vector<float> math_numbers;
|
||||||
|
std::vector<int> math_operations;
|
||||||
|
|
||||||
const uint32_t COLORS[] = {
|
std::map<int, char> operation_map = {
|
||||||
LEDColor::LED_COLOR_GREEN,
|
{0, '+'},
|
||||||
LEDColor::LED_COLOR_RED,
|
{1, '-'},
|
||||||
LEDColor::LED_COLOR_YELLOW,
|
{2, '*'},
|
||||||
LEDColor::LED_COLOR_BLUE,
|
{3, '/'},
|
||||||
LEDColor::LED_COLOR_PINK,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
int solved_times = 0;
|
int expression_answer = -1;
|
||||||
bool failed = false;
|
std::string display_expression;
|
||||||
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;
|
while (expression_answer < 0) {
|
||||||
for (int i = 0; i < 5; i++) {
|
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))};
|
||||||
set_unique_leds(led_options, indicator_numbers[i], COLORS[i]);
|
std::vector<int> possible_math_operations = {0, 1, 2, 3};
|
||||||
}
|
math_operations = unique_values(possible_math_operations, 3);
|
||||||
|
|
||||||
leds_flush();
|
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]));
|
||||||
|
}
|
||||||
|
|
||||||
std::uniform_int_distribution<> answer_color_dist(0, 4);
|
// Solve
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
std::map<int, std::string> color_name_map = {
|
bool found = false;
|
||||||
{0, "Green"},
|
for (int i = 0; i < math_operations.size(); i++) {
|
||||||
{1, "Red"},
|
if (math_operations[i] == 2) {
|
||||||
{2, "Yellow"},
|
// 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]));
|
||||||
{3, "Blue"},
|
math_numbers[i] = math_numbers[i] * math_numbers[i + 1];
|
||||||
{4, "Pink"},
|
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||||
};
|
math_operations.erase(math_operations.begin() + i);
|
||||||
|
found = true;
|
||||||
int answer_color = answer_color_dist(gen);
|
break;
|
||||||
|
} else if (math_operations[i] == 3) {
|
||||||
std::string color_string = color_name_map[answer_color];
|
// 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]));
|
||||||
std::string answer_num = std::to_string(indicator_numbers[answer_color]);
|
math_numbers[i] = math_numbers[i] / math_numbers[i + 1];
|
||||||
|
math_numbers.erase(math_numbers.begin() + i + 1);
|
||||||
// ESP_LOGI(TAG, "color string: %s", color_string.c_str());
|
math_operations.erase(math_operations.begin() + i);
|
||||||
|
found = true;
|
||||||
lcd_print(1, 2, 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(1, 2, color_string.c_str());
|
|
||||||
lcd_print(1, 3, entered_string.c_str());
|
|
||||||
}
|
|
||||||
if (get_module_time() <= 0) {
|
|
||||||
strike("Ran out of time!");
|
|
||||||
break;
|
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)};
|
||||||
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
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;
|
break;
|
||||||
|
|
||||||
}
|
|
||||||
if (!failed) {
|
|
||||||
solved_correctly = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// do WHAT
|
|
||||||
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
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
lcd_print(2, 1, display_expression.c_str());
|
}
|
||||||
|
|
||||||
// set LEDs
|
break;
|
||||||
const uint32_t COLORS[] = {
|
}
|
||||||
LEDColor::LED_COLOR_BLUE,
|
case 8: {
|
||||||
LEDColor::LED_COLOR_RED,
|
lcd_print(1, 1, "Plink");
|
||||||
LEDColor::LED_COLOR_GREEN,
|
set_module_time(TIME_PLINK);
|
||||||
LEDColor::LED_COLOR_YELLOW,
|
start_module_timer();
|
||||||
};
|
|
||||||
|
|
||||||
std::uniform_int_distribution<> add_sub_indicator_dist(1, 6);
|
std::uniform_int_distribution<> indicator_number_dist(0, 4);
|
||||||
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)};
|
// 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,
|
||||||
|
};
|
||||||
|
|
||||||
while ((((expression_answer + (modifier_indicators[0] * 3) - modifier_indicators[1]) * std::pow(3, modifier_indicators[2])) / std::pow(2, modifier_indicators[3])) < 0) {
|
int solved_times = 0;
|
||||||
modifier_indicators[0] = add_sub_indicator_dist(gen);
|
bool failed = false;
|
||||||
modifier_indicators[1] = add_sub_indicator_dist(gen);
|
while (solved_times < 3 && !failed) {
|
||||||
modifier_indicators[2] = mult_div_indicator_dist(gen);
|
int indicator_numbers[5] = {indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_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;
|
std::vector<int> led_options = all_leds;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
set_unique_leds(led_options, modifier_indicators[i], COLORS[i]);
|
set_unique_leds(led_options, indicator_numbers[i], COLORS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
leds_flush();
|
leds_flush();
|
||||||
|
|
||||||
std::string answer_string = std::to_string(expression_answer);
|
std::uniform_int_distribution<> answer_color_dist(0, 4);
|
||||||
std::string entered_string = "";
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Solved full answer: %s", answer_string.c_str());
|
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;
|
KeypadKey key;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (get_keypad_pressed(&key)) {
|
if (get_keypad_pressed(&key)) {
|
||||||
|
bool failed = false;
|
||||||
|
|
||||||
if (key == KeypadKey::star) {
|
if (key == KeypadKey::star) {
|
||||||
// clear
|
// clear
|
||||||
entered_string = "";
|
entered_string = "";
|
||||||
} else if (key == KeypadKey::pound) {
|
} else if (key == KeypadKey::pound) {
|
||||||
// submit
|
// submit
|
||||||
if (entered_string != answer_string) {
|
if (entered_string != answer_num) {
|
||||||
strike("Incorrect answer!");
|
strike("Incorrect answer!");
|
||||||
|
failed = true;
|
||||||
} else {
|
} else {
|
||||||
solved_correctly = true;
|
solved_correctly = true;;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
entered_string += char_of_keypad_key(key);
|
entered_string += char_of_keypad_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
lcd_clear();
|
lcd_clear();
|
||||||
lcd_print(1, 1, "What");
|
lcd_print(1, 1, "Plink");
|
||||||
lcd_print(2, 1, display_expression.c_str());
|
lcd_print(2, 1, color_string.c_str());
|
||||||
lcd_print(3, 1, entered_string.c_str());
|
lcd_print(3, 1, entered_string.c_str());
|
||||||
}
|
}
|
||||||
if (get_module_time() <= 0) {
|
if (get_module_time() <= 0) {
|
||||||
@ -891,26 +888,30 @@ void p001_step5(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default: {
|
}
|
||||||
ESP_LOGE(TAG, "Invalid puzzle number %d", puzzle);
|
if (!failed) {
|
||||||
|
solved_correctly = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
stop_module_timer();
|
stop_module_timer();
|
||||||
if (solved_correctly) {
|
if (solved_correctly) {
|
||||||
solved_puzzles++;
|
solved_puzzles++;
|
||||||
play_clip_wav(MOUNT_POINT "/correct.wav", true, false, 3, 0);
|
play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
|
||||||
vTaskDelay(pdMS_TO_TICKS(500));
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
solved_correctly = false;
|
solved_correctly = false;
|
||||||
} else {
|
} else {
|
||||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||||
}
|
}
|
||||||
clear_all_pressed_released();
|
play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
|
||||||
clean_bomb();
|
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 */
|
||||||
@ -1,11 +1,11 @@
|
|||||||
#include "p001_step6.h"
|
#include "step6.h"
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static const char *TAG = "step6";
|
static const char *TAG = "step6";
|
||||||
|
|
||||||
static uint8_t cut_wires = 0;
|
static uint8_t cut_wires = 0;
|
||||||
|
|
||||||
void p001_step6(void) {
|
void step6(void) {
|
||||||
get_cut_wires();
|
get_cut_wires();
|
||||||
clear_all_pressed_released();
|
clear_all_pressed_released();
|
||||||
|
|
||||||
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 */
|
||||||
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,,
|
||||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
resources/corr.wav
Normal file
BIN
resources/corr.wav
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
resources/correct.wav
Normal file
BIN
resources/correct.wav
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
resources/incorrect.wav
Normal file
BIN
resources/incorrect.wav
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user