diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 6391e62..86dacf2 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,5 +1,7 @@ -idf_component_register(SRCS "main.cpp" "drivers/TM1640/TM16xx.cpp" "drivers/TM1640/TM1640.cpp" "drivers/bottom_half.cpp" - INCLUDE_DIRS "") +idf_component_register(SRCS "main.cpp" + INCLUDE_DIRS ".") +add_subdirectory(drivers) +add_subdirectory(steps) set(EXTRA_COMPONENT_DIRS components) \ No newline at end of file diff --git a/main/drivers/CMakeLists.txt b/main/drivers/CMakeLists.txt new file mode 100644 index 0000000..989d0e7 --- /dev/null +++ b/main/drivers/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + "TM1640/TM16xx.cpp" + "TM1640/TM1640.cpp" + "bottom_half.cpp" +) + +target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES}) diff --git a/main/drivers/bottom_half.cpp b/main/drivers/bottom_half.cpp index 20b277b..f4dd878 100644 --- a/main/drivers/bottom_half.cpp +++ b/main/drivers/bottom_half.cpp @@ -17,6 +17,16 @@ static uint8_t touch_released; /// read buffer static uint8_t read_buf[8]; +void clear_all_pressed_released(void) { + keypad_pressed = 0; + button_pressed = 0; + touch_pressed = 0; + + keypad_released = 0; + button_released = 0; + touch_released = 0; +} + static bool _take_key(KeypadKey* kp, uint16_t* keypad_bitfield) { for (int i = 0; i < 16; i++) { int bit_selector = (1 << i); @@ -76,6 +86,66 @@ char char_of_keypad_key(KeypadKey kp) { } } +static bool _take_button(ButtonKey* button, uint16_t* button_bitfield) { + for (int i = 0; i < 4; i++) { + int bit_selector = (1 << (i+8)); + if ((*button_bitfield) & bit_selector) { + *button = (ButtonKey) i; + // clear bit + *button_bitfield &= ~bit_selector; + return true; + } + } + return false; +} +static bool _take_switch(SwitchKey* switch_, uint16_t* button_bitfield) { + for (int i = 0; i < 4; i++) { + int bit_selector = (1 << i); + if ((*button_bitfield) & bit_selector) { + *switch_ = (SwitchKey) i; + // clear bit + *button_bitfield &= ~bit_selector; + return true; + } + } + return false; +} + +bool get_pressed_button(ButtonKey* button) { + return _take_button(button, &button_pressed); +} +bool get_released_button(ButtonKey* button) { + return _take_button(button, &button_released); +} + +uint8_t get_button_state() { + return (uint8_t)((button_state >> 8) & 0xF); +} + +bool get_flipped_up_switch(SwitchKey* switch_) { + return _take_switch(switch_, &button_released); +} +bool get_flipped_down_switch(SwitchKey* switch_) { + return _take_switch(switch_, &button_released); +} +bool get_flipped_switch(SwitchKey* switch_) { + for (int i = 0; i < 4; i++) { + int bit_selector = (1 << (i+4)); + if (button_pressed & bit_selector || button_released & bit_selector) { + *switch_ = (SwitchKey) i; + // clear bit + button_pressed &= ~bit_selector; + button_released &= ~bit_selector; + return true; + } + } + return false; +} + +uint8_t get_switch_state(uint8_t* switch_flags) { + return (uint8_t)(button_state && 0xF); +} + static void poll_bottom_task(void *arg); // static void IRAM_ATTR gpio_isr_handler(void* arg) @@ -144,6 +214,7 @@ static void receive_button(void) { read_buf[1] = 0; ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, read_buf, 2, (100 / portTICK_PERIOD_MS))); uint16_t new_button_state = read_buf[0] | (read_buf[1] << 8); + ESP_LOGI("jjj", "New Button State: %d", new_button_state); uint16_t just_pressed = new_button_state & ~button_state; button_pressed |= just_pressed; diff --git a/main/drivers/bottom_half.h b/main/drivers/bottom_half.h index 5c7d484..6d6360e 100644 --- a/main/drivers/bottom_half.h +++ b/main/drivers/bottom_half.h @@ -31,10 +31,35 @@ typedef enum { kd = 15, } KeypadKey; +typedef enum { + b1 = 0, + b2 = 1, + b3 = 2, + b4 = 3, +} ButtonKey; + +typedef enum { + s1 = 0, + s2 = 1, + s3 = 2, + s4 = 3, +} SwitchKey; + +void clear_all_pressed_released(void); + bool get_pressed_keypad(KeypadKey* kp); bool get_released_keypad(KeypadKey* kp); char char_of_keypad_key(KeypadKey kp); +bool get_pressed_button(ButtonKey* button); +bool get_released_button(ButtonKey* button); +uint8_t get_button_state(); + +bool get_flipped_up_switch(SwitchKey* switch_); +bool get_flipped_down_switch(SwitchKey* switch_); +bool get_flipped_switch(SwitchKey* switch_); +uint8_t get_switch_state(uint8_t* switch_flags); + static void poll_bottom_task(void *arg); void init_bottom_half(); diff --git a/main/main.cpp b/main/main.cpp index 15936c0..4955884 100755 --- a/main/main.cpp +++ b/main/main.cpp @@ -15,7 +15,12 @@ #include "steps/step0.hpp" #include "steps/step1.hpp" - +#include "steps/step2.hpp" +#include "steps/step3.hpp" +#include "steps/step4.hpp" +#include "steps/step5.hpp" +#include "steps/step6.hpp" + #define WIRES_ADDR 125 #define WIRES_REG_WIRES @@ -38,6 +43,11 @@ extern "C" void app_main(void) { step0(); step1(); + step2(); + step3(); + step4(); + step5(); + step6(); // create_demo_ui(); diff --git a/main/steps/CMakeLists.txt b/main/steps/CMakeLists.txt new file mode 100644 index 0000000..20a414f --- /dev/null +++ b/main/steps/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES + "wires_puzzle.cpp" +) + +target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES}) diff --git a/main/steps/setup_wires.hpp b/main/steps/setup_wires.hpp new file mode 100644 index 0000000..08e3591 --- /dev/null +++ b/main/steps/setup_wires.hpp @@ -0,0 +1,93 @@ +#ifndef SETUP_WIRES_HPP +#define SETUP_WIRES_HPP + +#include "../drivers/bottom_half.h" +#include "../drivers/char_lcd.hpp" +#include "wires_puzzle.h" +#include + +void setup_wires(void) { + clear_all_pressed_released(); + lcd_clear(&lcd); + + WiresColor wires[NUM_WIRES]; + load_wires_from_sd_card(wires); + + bool cut[NUM_WIRES]; + solve_wires(wires, cut); + + lcd_set_cursor(&lcd, 1, 1); + char wires_string[NUM_WIRES+1] = {0}; + wires_to_string(wires, wires_string); + lcd_print(&lcd, wires_string); + + lcd_set_cursor(&lcd, 1, 2); + cut_to_string(cut, wires_string); + lcd_print(&lcd, wires_string); + lcd_set_cursor(&lcd, 1, 1); + + int editing_idx = 0; + + KeypadKey key; + ButtonKey button; + + while (1) { + while (get_pressed_keypad(&key)) { + if (key == KeypadKey::k1) { + generate_new_wires(wires); + save_wires_to_sd_card(wires); + bool cut[NUM_WIRES]; + solve_wires(wires, cut); + + lcd_set_cursor(&lcd, 1, 1); + char wires_string[NUM_WIRES+1] = {0}; + wires_to_string(wires, wires_string); + lcd_print(&lcd, wires_string); + + lcd_set_cursor(&lcd, 1, 2); + cut_to_string(cut, wires_string); + lcd_print(&lcd, wires_string); + lcd_set_cursor(&lcd, editing_idx+1, 1); + } else if (key == KeypadKey::pound) { + lcd_no_cursor(&lcd); + return; + } + } + + while (get_pressed_button(&button)) { + ESP_LOGI("setup_wires", "Button press: %d", (int)button); + if (button == ButtonKey::b1) { + // left + editing_idx = editing_idx - 1; + if (editing_idx < 0) editing_idx = 0; + } else if (button == ButtonKey::b4) { + // right + editing_idx = editing_idx + 1; + if (editing_idx >= NUM_WIRES) editing_idx = NUM_WIRES-1; + } else if (button == ButtonKey::b2) { + // down + wires[editing_idx] = (WiresColor)(((int)(wires[editing_idx]) + (NUM_COLORS-1)) % NUM_COLORS); + } else if (button == ButtonKey::b3) { + // up + wires[editing_idx] = (WiresColor)(((int)(wires[editing_idx]) + 1) % NUM_COLORS); + } + save_wires_to_sd_card(wires); + lcd_set_cursor(&lcd, 1, 1); + char wires_string[NUM_WIRES+1] = {0}; + wires_to_string(wires, wires_string); + lcd_print(&lcd, wires_string); + + bool cut[NUM_WIRES]; + solve_wires(wires, cut); + lcd_set_cursor(&lcd, 1, 2); + cut_to_string(cut, wires_string); + lcd_print(&lcd, wires_string); + + lcd_set_cursor(&lcd, editing_idx+1, 1); + } + + vTaskDelay(pdMS_TO_TICKS(10)); + } +} + +#endif /* SETUP_WIRES_HPP */ \ No newline at end of file diff --git a/main/steps/step0.hpp b/main/steps/step0.hpp index e0a4a16..4525b00 100644 --- a/main/steps/step0.hpp +++ b/main/steps/step0.hpp @@ -4,6 +4,7 @@ #include "../drivers/bottom_half.h" #include "../drivers/char_lcd.hpp" #include "../drivers/wires.hpp" +#include "setup_wires.hpp" static const char *STEP0_TAG = "step0"; @@ -26,16 +27,24 @@ void step0(void) { current_idx = 1; } else if (key == KeypadKey::pound) { // submit - if (strcmp(current, "*9819") == 0) { + if (current[0] == '\0') { + // do nothing + } else if (strcmp(current, "*9819") == 0) { lcd_set_cursor(&lcd, 1, 2); lcd_print(&lcd, "Diffusal Initated"); vTaskDelay(pdMS_TO_TICKS(2000)); lcd_clear(&lcd); return; + } else if (strcmp(current, "*59861") == 0) { + lcd_set_cursor(&lcd, 1, 2); + lcd_print(&lcd, "Set Up Wires"); + vTaskDelay(pdMS_TO_TICKS(2000)); + setup_wires(); } else { lcd_set_cursor(&lcd, 1, 2); lcd_print(&lcd, "Invalid Star Code"); strike("Invalid Star Code"); + vTaskDelay(pdMS_TO_TICKS(2000)); } // clear @@ -43,8 +52,6 @@ void step0(void) { current[i] = 0; } current_idx = 0; - - vTaskDelay(pdMS_TO_TICKS(2000)); } else { // out of room. skip if (current_idx >= STRING_MAX_LEN) break; @@ -60,10 +67,6 @@ void step0(void) { lcd_set_cursor(&lcd, 1, 1); lcd_print(&lcd, current); } - while (get_released_keypad(&key)) { - char c = char_of_keypad_key(key); - // ESP_LOGI(STEP0_TAG, "Released: %c", c); - } vTaskDelay(pdMS_TO_TICKS(10)); } diff --git a/main/steps/wires_puzzle.cpp b/main/steps/wires_puzzle.cpp new file mode 100644 index 0000000..28c3c33 --- /dev/null +++ b/main/steps/wires_puzzle.cpp @@ -0,0 +1,347 @@ +#include "wires_puzzle.h" + +const static char* WIRES_FILE_PATH = "/sdcard/wires.txt"; +const static char* TAG = "wires_puzzle"; + +static int color_name_len[NUM_COLORS] = { + 3, // red + 6, // yellow + 5, // green + 4, // blue + 5, // black + 5, // white +}; + +static int max(int n0, int n1, int n2, int n3, int n4, int n5); + +void wires_to_string(WiresColor* wires, char* out_wires_string) { + for (int i = 0; i < NUM_WIRES; i++) { + switch (wires[i]) { + case WiresColor::red: + out_wires_string[i] = 'r'; + break; + case WiresColor::yellow: + out_wires_string[i] = 'y'; + break; + case WiresColor::green: + out_wires_string[i] = 'g'; + break; + case WiresColor::blue: + out_wires_string[i] = 'u'; + break; + case WiresColor::black: + out_wires_string[i] = 'b'; + break; + case WiresColor::white: + out_wires_string[i] = 'w'; + break; + } + } +} +void cut_to_string(bool* cut, char* out_cut_string) { + for (int i = 0; i < NUM_WIRES; i++) { + if (cut[i]) { + out_cut_string[i] = 'c'; + } else { + out_cut_string[i] = ' '; + } + } +} + +void string_to_wires(char* wires_string, WiresColor* out_wires) { + for (int i = 0; i < NUM_WIRES; i++) { + switch (wires_string[i]) { + case 'r': + out_wires[i] = WiresColor::red; + break; + case 'y': + out_wires[i] = WiresColor::yellow; + break; + case 'g': + out_wires[i] = WiresColor::green; + break; + case 'u': + out_wires[i] = WiresColor::blue; + break; + case 'b': + out_wires[i] = WiresColor::black; + break; + case 'w': + out_wires[i] = WiresColor::white; + break; + } + } +} + +void save_wires_to_sd_card(WiresColor* wires) { + FILE* f = fopen(WIRES_FILE_PATH, "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open wires file to write"); + } + char wires_string[NUM_WIRES+1] = {0}; + wires_to_string(wires, wires_string); + fprintf(f, wires_string); + fclose(f); +} + +void load_wires_from_sd_card(WiresColor* out_wires) { + FILE* f = fopen(WIRES_FILE_PATH, "r"); + if (f == NULL) { + ESP_LOGW(TAG, "Failed to read wires file. Generating new wires"); + generate_new_wires(out_wires); + save_wires_to_sd_card(out_wires); + return; + } + char wires_string[NUM_WIRES+1] = {0}; + fgets(wires_string, sizeof(wires_string), f); + fclose(f); + + string_to_wires(wires_string, out_wires); +} + +/// @brief Fills the array with 6 random WiresColors. +/// @param wires and array of len >= 6 to be populated with random colors +void generate_new_wires(WiresColor* wires) { + for (int w = 0; w < NUM_WIRES; w++) { + // roughly evenly distributed + uint32_t rand = esp_random() % NUM_COLORS; + wires[w] = (WiresColor)(rand); + } + + bool cuts[NUM_WIRES] = {0}; + solve_wires(wires, cuts); + int num_cuts = 0; + for (int i = 0; i < NUM_WIRES; i++) { + if (cuts[i]) num_cuts++; + } + + // regenerate if there are less than 3 cuts. + if (num_cuts < 3) { + return generate_new_wires(wires); + } +} + +void solve_wires(WiresColor* wires, bool* out_cut) { + // by default, don't cut any wires + for (int i = 0; i < NUM_WIRES; i++) { + out_cut[i] = false; + } + + // Find all positions of all wire colors + int red_pos[NUM_WIRES + 1] = {0}; + int red_pos_len = 0; + int yellow_pos[NUM_WIRES + 1] = {0}; + int yellow_pos_len = 0; + int green_pos[NUM_WIRES + 1] = {0}; + int green_pos_len = 0; + int blue_pos[NUM_WIRES + 1] = {0}; + int blue_pos_len = 0; + int black_pos[NUM_WIRES + 1] = {0}; + int black_pos_len = 0; + int white_pos[NUM_WIRES + 1] = {0}; + int white_pos_len = 0; + + for (int i = 0; i < NUM_WIRES; i++) { + if (wires[i] == WiresColor::red) { + red_pos[red_pos_len++] = i; + } else if (wires[i] == WiresColor::yellow) { + yellow_pos[yellow_pos_len++] = i; + } else if (wires[i] == WiresColor::green) { + green_pos[green_pos_len++] = i; + } else if (wires[i] == WiresColor::blue) { + blue_pos[blue_pos_len++] = i; + } else if (wires[i] == WiresColor::black) { + black_pos[black_pos_len++] = i; + } else if (wires[i] == WiresColor::white) { + white_pos[white_pos_len++] = i; + } + } + + int* list_pos[NUM_COLORS] = { + red_pos, + yellow_pos, + green_pos, + blue_pos, + black_pos, + white_pos + }; + int list_pos_len[NUM_COLORS] = { + red_pos_len, + yellow_pos_len, + green_pos_len, + blue_pos_len, + black_pos_len, + white_pos_len + }; + + // CUT CHECKS + + // cut the second wire of all most common colors + int max_len = max(red_pos_len, yellow_pos_len, green_pos_len, blue_pos_len, black_pos_len, white_pos_len); + if (max_len >= 2) { + for (int i = 0; i < NUM_COLORS; i++) { + if (list_pos_len[i] == max_len) { + int idx = list_pos[i][1]; + out_cut[idx] = true; + } + } + } + + // cut the first wire if it is green or white + if (wires[0] == WiresColor::green || wires[0] == WiresColor::white) out_cut[0] = true; + + // cut blue wires in even positions (odd indexes) + for (int i = 1; i < NUM_WIRES; i += 2) { + if (wires[i] == WiresColor::blue) out_cut[i] = true; + } + + // cut black and yellow wires next to black and yellow wires + for (int i = 0; i < NUM_WIRES-1; i++) { + if ( + (wires[i] == WiresColor::yellow || wires[i] == WiresColor::black) && + (wires[i+1] == WiresColor::yellow || wires[i+1] == WiresColor::black) + ) { + out_cut[i] = true; + out_cut[1] = true; + } + } + + // cut the last green wire next to a yellow or white wire + for (int green_idx = green_pos_len-1; green_idx >= 0; green_idx--) { + int pos = green_pos[green_idx]; + if ( + wires[pos-1] == WiresColor::yellow || + wires[pos-1] == WiresColor::white || + wires[pos+1] == WiresColor::yellow || + wires[pos+1] == WiresColor::white + ) { + out_cut[pos] = true; + break; + } + } + + // cut all white wires if there is a red wire in the 5th position + if (wires[4] == WiresColor::red) { + for (int white_idx = 0; white_idx < white_pos_len; white_idx++) { + out_cut[white_pos[white_idx]] = true; + } + } + + // cut the first black wire if there are more white wires than green wires + if (white_pos_len > green_pos_len && black_pos_len > 0) { + out_cut[black_pos[0]] = true; + } + + // cut all wires in an alternating pattern of 2 colors at least 4 wires long + for (int i = 0; i < NUM_WIRES-3; i++) { + if ( + wires[i] == wires[i+2] && + wires[i+1] == wires[i+3] && + wires[i] == wires[i+1] + ) { + out_cut[i] = true; + out_cut[i+1] = true; + out_cut[i+2] = true; + out_cut[i+3] = true; + } + } + + // cut any wires if their position matches the number of letters in the color's name + for (int i = 0; i < NUM_WIRES; i++) { + if (color_name_len[i] == i+1) { + out_cut[i] = true; + } + } + + // cut all red wires if there are no more than 2 wires of the same color + if (max_len <= 2) { + for (int i = 0; i < red_pos_len; i++) { + out_cut[red_pos[i]] = true; + } + } + + // cut the last wire if it is the same color as the first wire + if (wires[0] == wires[NUM_WIRES-1]) { + out_cut[NUM_WIRES-1] = true; + } + + // cut any wire adjacent to both a yellow and blue wire + for (int i = 0; i < NUM_WIRES-2; i++) { + if ( + (wires[i] == WiresColor::yellow && wires[i+2] == WiresColor::blue) || + (wires[i] == WiresColor::blue && wires[i+2] == WiresColor::white) + ) { + out_cut[i+1] = true; + } + } + + // NEVER CUT + + // never cut blue wires next to red or green wires + for (int i = 0; i < blue_pos_len; i++) { + int pos = blue_pos[i]; + if ( + wires[pos-1] == WiresColor::red || + wires[pos-1] == WiresColor::green || + wires[pos+1] == WiresColor::red || + wires[pos+1] == WiresColor::green + ) { + out_cut[pos] = false; + } + } + + // never cut white wires if there is at least one red, black and green wire + if (red_pos_len > 0 && green_pos_len > 0 && black_pos_len > 0) { + for (int i = 0; i < white_pos_len; i++) { + out_cut[white_pos[i]] = false; + } + } + + // never cut red or black wires in the 4th or 7th positions + if (wires[3] == WiresColor::red || wires[3] == WiresColor::black) { + out_cut[3] = false; + } + if (wires[6] == WiresColor::red || wires[6] == WiresColor::black) { + out_cut[6] = false; + } + + // never cut wires that have the same color on both sides of it + for (int i = 0; i < NUM_WIRES-2; i++) { + if (wires[i] == wires[i+2]) { + out_cut[i+1] = false; + } + } + + // never cut a wire in the 1st, 2nd, or 3rd position if it is the same color as the wire in the 4th position. + if (wires[0] == wires[3]) { + out_cut[0] = false; + } + if (wires[1] == wires[3]) { + out_cut[1] = false; + } + if (wires[2] == wires[3]) { + out_cut[2] = false; + } + + // never cut a blue or green wire in the 8th postion + if (wires[7] == WiresColor::blue || wires[7] == WiresColor::green) { + out_cut[7] = false; + } + + // never cut a wire in the 5th position if there are no yellow wires + if (yellow_pos_len == 0) { + out_cut[4] = false; + } +} + +static int max(int n0, int n1, int n2, int n3, int n4, int n5) { + int max = n0; + + if (n1 > max) max = n1; + if (n2 > max) max = n2; + if (n3 > max) max = n3; + if (n4 > max) max = n4; + if (n5 > max) max = n5; + + return max; +} diff --git a/main/steps/wires_puzzle.h b/main/steps/wires_puzzle.h new file mode 100644 index 0000000..57b3fe2 --- /dev/null +++ b/main/steps/wires_puzzle.h @@ -0,0 +1,40 @@ +#ifndef WIRES_PUZZLE_H +#define WIRES_PUZZLE_H + +#include +#include +#include +#include "esp_vfs_fat.h" + +#define NUM_COLORS 6 +#define NUM_WIRES 8 + +typedef enum { + red = 0, + yellow = 1, + green = 2, + blue = 3, + black = 4, + white = 5, +} WiresColor; + +void solve_wires(WiresColor* wires, bool* out_cut); +void generate_new_wires(WiresColor* wires); + +void cut_to_string(bool* cut, char* out_cut_string); +void wires_to_string(WiresColor* wires, char* out_wires_string); +void string_to_wires(char* wires_string, WiresColor* out_wires); + +void save_wires_to_sd_card(WiresColor* wires); +void load_wires_from_sd_card(WiresColor* out_wires); + +/// @brief Fills the array with 6 random WiresColors. +/// @param wires the array of len >= 6 to be populated with random colors +void generate_new_wires(WiresColor* wires); + +/// @brief Solves the wires puzzle, setting `out_cut` according to which wires are cut. +/// @param wires the array of len >= 6 as input to the solver +/// @param out_cut the output array of len >= 6 for which wires to cut +void solve_wires(WiresColor* wires, bool* out_cut); + +#endif /* WIRES_PUZZLE_H */ \ No newline at end of file