diff --git a/README.md b/README.md index 1ade192..2b18ad8 100644 --- a/README.md +++ b/README.md @@ -68,4 +68,6 @@ Some things we had to consider: ## Development setup +In the Arduino IDE, select the correct board (Arduino Nano) and processor (ATmega328P (Old Bootloader)). + We use [this](https://github.com/autowp/arduino-mcp2515/) library for CAN communications. See [this](https://github.com/autowp/arduino-mcp2515/#software-usage) header for 3 simple steps on how to use it in the arduino IDE diff --git a/lib/obus_can.cpp b/lib/obus_can.cpp index d242019..0deb09a 100644 --- a/lib/obus_can.cpp +++ b/lib/obus_can.cpp @@ -124,6 +124,10 @@ bool receive(struct message *msg) { break; case OBUS_PAYLDTYPE_COUNT: + if (receive_frame.can_dlc < 2) { + Serial.println(F("W Received illegal count msg: payload <2")); + return false; + } msg->count = receive_frame.data[1]; break; @@ -156,7 +160,8 @@ void send(struct message *msg) { uint8_t length = 1; send_frame.data[0] = msg->msg_type; - switch (payload_type(msg->from.type, msg->msg_type)) { + uint8_t pyld_type = payload_type(msg->from.type, msg->msg_type); + switch (pyld_type) { case OBUS_PAYLDTYPE_EMPTY: break; @@ -176,7 +181,8 @@ void send(struct message *msg) { break; default: - Serial.println(F("Unknown payload type")); + Serial.print(F("E Unknown payload type ")); + Serial.println(pyld_type); return; } diff --git a/lib/obus_module.cpp b/lib/obus_module.cpp index 79dc3ec..25a293d 100644 --- a/lib/obus_module.cpp +++ b/lib/obus_module.cpp @@ -42,7 +42,7 @@ void _updateLed() { digitalWrite(GREEN_LED, false); } - blink_next_time = millis() + BLINK_DELAY_SLOW; + blink_next_time = millis() + blink_delay; } } @@ -71,7 +71,7 @@ void setup(uint8_t type, uint8_t id) { obus_can::init(); strike_count = 0; - active = false; + active = true; pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); @@ -142,7 +142,7 @@ void strike() { return; } strike_count++; - _setLedBlink(COLOR_RED, BLINK_DELAY_FAST); + digitalWrite(RED_LED, HIGH); time_stop_strike_led = millis() + 2000; obus_can::send_m_strike(this_module, strike_count); } diff --git a/misc/reverse_engineered_hardware/month_day_selector/month_day_selector.ino b/misc/reverse_engineered_hardware/month_day_selector/month_day_selector.ino index eb819ad..eef29eb 100644 --- a/misc/reverse_engineered_hardware/month_day_selector/month_day_selector.ino +++ b/misc/reverse_engineered_hardware/month_day_selector/month_day_selector.ino @@ -1,11 +1,11 @@ // yellow -#define CLOCK_PIN 8 +#define CLOCK_PIN 6 // orange -#define DATA_PIN 10 +#define DATA_PIN 7 // green -#define READ_PIN 7 +#define READ_PIN 5 uint8_t shiftInFixed(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { diff --git a/src/controller/controller.ino b/src/controller/controller.ino index 2730d6d..b0a4008 100644 --- a/src/controller/controller.ino +++ b/src/controller/controller.ino @@ -5,9 +5,10 @@ #define STATE_INACTIVE 0 #define STATE_HELLO 1 #define STATE_GAME 2 +#define STATE_GAMEOVER 3 #define OBUS_MAX_STRIKES 3 // Number of strikes allowed until game over -#define OBUS_GAME_DURATION 10 // Duration of the game in seconds +#define OBUS_GAME_DURATION 60 // Duration of the game in seconds #define OBUS_MAX_MODULES 16 #define OBUS_DISC_DURATION 5 // Duration of discovery round in seconds @@ -27,13 +28,14 @@ uint8_t nr_connected_puzzles; uint8_t strikes; // Bitvector for checking if game is solved or not -// 32 bits per uint32 bitvector field -#define N_UNSOLVED_PUZZLES DIVIDE_CEIL(MAX_AMOUNT_PUZZLES, 32) -uint32_t unsolved_puzzles[N_UNSOLVED_PUZZLES]; +// 8 bits per uint8 bitvector field +#define N_UNSOLVED_PUZZLES DIVIDE_CEIL(MAX_AMOUNT_PUZZLES, 8) +uint8_t unsolved_puzzles[N_UNSOLVED_PUZZLES]; // Timers uint32_t hello_round_start; uint32_t game_start; +uint32_t last_draw; uint32_t last_update; struct obus_can::module this_module = { @@ -51,7 +53,7 @@ TM1638plus tm(STROBE_TM, CLOCK_TM , DIO_TM, HI_FREQ); void setup() { - Serial.begin(9600); + Serial.begin(19200); obus_can::init(); state = STATE_INACTIVE; @@ -70,14 +72,14 @@ bool check_solved() { } -void add_module_to_bit_vector(uint8_t module_id) { +void add_puzzle_to_bit_vector(uint8_t module_id) { uint8_t byte_index = module_id >> 3; uint8_t bit_index = module_id & 0x07; unsolved_puzzles[byte_index] |= 0x1 << bit_index; } -void solve_module_in_bit_vector(uint8_t module_id) { +void solve_puzzle_in_bit_vector(uint8_t module_id) { uint8_t byte_index = module_id >> 3; uint8_t bit_index = module_id & 0x07; unsolved_puzzles[byte_index] &= ~(0x1 << bit_index); @@ -104,8 +106,8 @@ void start_hello() { uint16_t full_module_id(struct obus_can::module mod) { return \ - ((uint16_t) mod.type << 8) | \ - (uint16_t) mod.id; + (((uint16_t) mod.type) << 8) | \ + ((uint16_t) mod.id); } @@ -115,17 +117,23 @@ void receive_hello() { if (obus_can::receive(&msg)) { if (msg.msg_type == OBUS_MSGTYPE_M_HELLO) { - Serial.print(" Registered module "); - Serial.println(full_module_id(msg.from)); - if (nr_connected_modules < OBUS_MAX_MODULES) { + Serial.print(F(" Registered module ")); + Serial.println(full_module_id(msg.from)); + connected_modules_ids[nr_connected_modules] = msg.from; nr_connected_modules++; if (msg.from.type == OBUS_TYPE_PUZZLE) { nr_connected_puzzles++; - add_module_to_bit_vector(full_module_id(msg.from)); + add_puzzle_to_bit_vector(msg.from.id); } + + char buffer[10]; + snprintf(buffer, 10, "%02d oF %02d", nr_connected_modules, OBUS_MAX_MODULES); + tm.displayText(buffer); + } else { + Serial.println(F("W Max # modules reached")); } obus_can::send_c_ack(this_module); @@ -135,10 +143,12 @@ void receive_hello() { } else if (current_time - hello_round_start > OBUS_DISC_DURATION_MS) { if (nr_connected_puzzles == 0) { hello_round_start = current_time; - Serial.println(" No puzzle modules found, restarting discovery round"); + obus_can::send_c_hello(this_module); + Serial.println(F(" No puzzle modules, resend hello")); + } else { + Serial.println(F(" End of discovery round")); + initialize_game(); } - Serial.println(" End of discovery round"); - initialize_game(); } } @@ -147,11 +157,13 @@ void initialize_game() { strikes = 0; game_start = millis(); + last_draw = 0; last_update = game_start; state = STATE_GAME; Serial.println(" Game started"); + draw_display(millis(), OBUS_GAME_DURATION_MS); obus_can::send_c_gamestart(this_module, OBUS_GAME_DURATION_MS, strikes, OBUS_MAX_STRIKES); } @@ -168,7 +180,7 @@ void receive_module_update() { break; case OBUS_MSGTYPE_M_SOLVED: - solve_module_in_bit_vector(full_module_id(msg.from)); + solve_puzzle_in_bit_vector(full_module_id(msg.from)); break; default: @@ -181,6 +193,22 @@ void receive_module_update() { } +void draw_display(uint32_t current_time, uint32_t time_left) { + if (last_draw + 100 <= current_time) { + // +25 to avoid rounding down when the loop runs early + int totaldecisec = (time_left + 25) / 100; + int decisec = totaldecisec % 10; + int seconds = (totaldecisec / 10) % 60; + int minutes = (totaldecisec / 10 / 60) % 60; + int hours = totaldecisec / 10 / 60 / 60; + char buffer[10]; + snprintf(buffer, 10, "%01dh%02d %02d.%01d", hours, minutes, seconds, decisec); + tm.displayText(buffer); + last_draw = current_time; + } +} + + void game_loop() { uint32_t current_time = millis(); uint32_t time_elapsed = current_time - game_start; @@ -193,34 +221,36 @@ void game_loop() { if (check_solved()) { Serial.println(" Game solved"); obus_can::send_c_solved(this_module, time_left, strikes, OBUS_MAX_STRIKES); - state = STATE_INACTIVE; + state = STATE_GAMEOVER; tm.displayText("dISArmEd"); return; } if (time_left == 0) { Serial.println(" Time's up"); obus_can::send_c_timeout(this_module, time_left, strikes, OBUS_MAX_STRIKES); - state = STATE_INACTIVE; - tm.displayText("boom"); + state = STATE_GAMEOVER; + tm.displayText(" boo t"); + // m + tm.display7Seg(4, 0b01010100); + tm.display7Seg(5, 0b01000100); return; } if (strikes >= OBUS_MAX_STRIKES) { Serial.println(" Strikeout"); obus_can::send_c_strikeout(this_module, time_left, strikes, OBUS_MAX_STRIKES); - state = STATE_INACTIVE; - tm.displayText("boom"); + state = STATE_GAMEOVER; + tm.displayText(" boo S"); + // m + tm.display7Seg(4, 0b01010100); + tm.display7Seg(5, 0b01000100); return; } + draw_display(current_time, time_left); + if (last_update + OBUS_UPDATE_INTERVAL <= current_time) { obus_can::send_c_state(this_module, time_left, strikes, OBUS_MAX_STRIKES); last_update = current_time; - - int totalsec = (current_time + 100) / 1000; - int minutes = totalsec / 60; - char buffer[10]; - snprintf(buffer, 10, "%06d.%02d", minutes, totalsec % 60); - tm.displayText(buffer); } } @@ -238,5 +268,8 @@ void loop() { case STATE_GAME: game_loop(); break; + + case STATE_GAMEOVER: + break; } } diff --git a/src/modules/testmodule_buttons/testmodule_buttons.ino b/src/modules/testmodule_buttons/testmodule_buttons.ino index 501e17c..19223ad 100644 --- a/src/modules/testmodule_buttons/testmodule_buttons.ino +++ b/src/modules/testmodule_buttons/testmodule_buttons.ino @@ -9,8 +9,7 @@ ezButton green_button(6); void setup() { Serial.begin(115200); - // WARNING: do not use 255 for your module - obus_module::setup(OBUS_TYPE_PUZZLE, 255); + obus_module::setup(OBUS_TYPE_PUZZLE, OBUS_PUZZLE_ID_DEVELOPMENT); red_button.setDebounceTime(100); green_button.setDebounceTime(100); } diff --git a/src/modules/testmodule_needy_buttons/testmodule_needy_buttons.ino b/src/modules/testmodule_needy_buttons/testmodule_needy_buttons.ino index ab6b8df..a58e648 100644 --- a/src/modules/testmodule_needy_buttons/testmodule_needy_buttons.ino +++ b/src/modules/testmodule_needy_buttons/testmodule_needy_buttons.ino @@ -10,8 +10,7 @@ ezButton green_button(6); void setup() { Serial.begin(115200); - // WARNING: do not use 255 for your module - obus_module::setup(OBUS_TYPE_NEEDY, 255); + obus_module::setup(OBUS_TYPE_NEEDY, OBUS_NEEDY_ID_DEVELOPMENT); green_button.setDebounceTime(100); }