diff --git a/src/controller/controller.ino b/src/controller/controller.ino index 87ec1d4..3208d10 100644 --- a/src/controller/controller.ino +++ b/src/controller/controller.ino @@ -6,22 +6,27 @@ #define STATE_HELLO 1 #define STATE_GAME 2 -#define OBUS_MAX_STRIKES 3 // Number of strikes allowed until game over +#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_MS ((uint32_t) OBUS_GAME_DURATION*1000) +#define DIVIDE_CEIL(dividend, divisor) ((dividend + (divisor - 1)) / divisor) +#define MAX_AMOUNT_PUZZLES 256 // The ID of a puzzle is uint8 + uint8_t state = STATE_INACTIVE; struct module connected_modules_ids[OBUS_MAX_MODULES]; uint8_t nr_connected_modules; uint8_t strikes; -// Bit vectors for checking if game is solved or not -uint8_t unsolved_puzzles[32]; // 256 bits +// 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]; -// TIMERS +// Timers uint32_t hello_round_start; uint32_t game_start; uint32_t last_update; @@ -40,15 +45,13 @@ void setup() { } -uint8_t check_solved() { - uint8_t solved = 1; - for (uint8_t i = 0; i < 32; i++) { - if (unsolved_puzzles != 0) { - solved = 0; - break; +bool check_solved() { + for (uint8_t i = 0; i < N_UNSOLVED_PUZZLES; i++) { + if (unsolved_puzzles[i] != 0) { + return false; } } - return solved; + return true; } @@ -72,20 +75,13 @@ void start_hello() { nr_connected_modules = 0; // Zero bit vectors - for (uint8_t i = 0; i < 32; i++) { + for (uint8_t i = 0; i < N_UNSOLVED_PUZZLES; i++) { unsolved_puzzles[i] = 0; } - struct obus_message msg = obuscan_msg_c_hello(this_module); - obuscan_send(&msg); + obuscan_send_c_hello(this_module); - Serial.println(F("Start of discovery round")); -} - - -void send_ack() { - struct obus_message msg = obuscan_msg_c_ack(this_module); - obuscan_send(&msg); + Serial.println(F(" Start of discovery round")); } @@ -94,8 +90,8 @@ void receive_hello() { uint32_t current_time = millis(); if (obuscan_receive(&msg)) { - if (msg.msg_type == OBUS_MSGTYPE_M_HELLO) { - Serial.print("Registered module "); + if (msg.msg_type == OBUS_MSGTYPE_M_HELLO) { + Serial.print(" Registered module "); Serial.println(full_module_id(msg.from)); connected_modules_ids[nr_connected_modules] = msg.from; nr_connected_modules++; @@ -104,11 +100,11 @@ void receive_hello() { add_module_to_bit_vector(full_module_id(msg.from)); } - send_ack(); - Serial.println("ACK"); + obuscan_send_c_ack(this_module); + Serial.println(" ACK"); } } else if (current_time - hello_round_start > OBUS_DISC_DURATION_MS) { - Serial.println("End of discovery round"); + Serial.println(" End of discovery round"); initialize_game(); } } @@ -121,9 +117,9 @@ void initialize_game() { last_update = game_start; state = STATE_GAME; - Serial.println("Game started"); + Serial.println(" Game started"); - send_game_update(OBUS_MSGTYPE_C_GAMESTART, OBUS_GAME_DURATION_MS); + obuscan_send_c_gamestart(this_module, OBUS_GAME_DURATION_MS, strikes, OBUS_MAX_STRIKES); } @@ -152,22 +148,6 @@ void receive_module_update() { } -void send_game_update(uint8_t msg_type, uint32_t time_left) { - Serial.print(F("Send ")); - Serial.print(msg_type); - Serial.print(F(": ")); - Serial.print(time_left); - Serial.print(F(", ")); - Serial.print(strikes); - Serial.print(F("/")); - Serial.println(OBUS_MAX_STRIKES); - - struct obus_message msg = obuscan_msg_c_payld_gamestatus( - this_module, false, msg_type, time_left, strikes, OBUS_MAX_STRIKES); - obuscan_send(&msg); -} - - void game_loop() { uint32_t current_time = millis(); uint32_t time_elapsed = current_time - game_start; @@ -178,24 +158,26 @@ void game_loop() { receive_module_update(); if (check_solved()) { - Serial.println("Game solved"); - send_game_update(OBUS_MSGTYPE_C_SOLVED, time_left); + Serial.println(" Game solved"); + obuscan_send_c_solved(this_module, time_left, strikes, OBUS_MAX_STRIKES); state = STATE_INACTIVE; return; - } else if (time_left == 0) { - Serial.println("Time's up"); - send_game_update(OBUS_MSGTYPE_C_TIMEOUT, time_left); + } + if (time_left == 0) { + Serial.println(" Time's up"); + obuscan_send_c_timeout(this_module, time_left, strikes, OBUS_MAX_STRIKES); state = STATE_INACTIVE; return; - } else if (strikes >= OBUS_MAX_STRIKES) { - Serial.println("Strikeout"); - send_game_update(OBUS_MSGTYPE_C_STRIKEOUT, time_left); + } + if (strikes >= OBUS_MAX_STRIKES) { + Serial.println(" Strikeout"); + obuscan_send_c_strikeout(this_module, time_left, strikes, OBUS_MAX_STRIKES); state = STATE_INACTIVE; return; } if (last_update + OBUS_UPDATE_INTERVAL <= current_time) { - send_game_update(OBUS_MSGTYPE_C_STATE, time_left); + obuscan_send_c_state(this_module, time_left, strikes, OBUS_MAX_STRIKES); last_update = current_time; } } diff --git a/src/controller/obus_can.cpp b/src/controller/obus_can.cpp index e9f2964..0041ff0 100644 --- a/src/controller/obus_can.cpp +++ b/src/controller/obus_can.cpp @@ -1,5 +1,4 @@ #include -#include #include "obus_can.hpp" @@ -29,6 +28,42 @@ void _decode_can_id(uint16_t can_id, struct module *mod, bool *priority) { assert(mod->type <= 0b11); } +uint8_t obuscan_payload_type(uint8_t module_type, uint8_t msg_type) { + if (module_type == OBUS_TYPE_CONTROLLER) { + switch (msg_type) { + case OBUS_MSGTYPE_C_ACK: + case OBUS_MSGTYPE_C_HELLO: + return OBUS_PAYLDTYPE_EMPTY; + + case OBUS_MSGTYPE_C_GAMESTART: + case OBUS_MSGTYPE_C_STATE: + case OBUS_MSGTYPE_C_SOLVED: + case OBUS_MSGTYPE_C_TIMEOUT: + case OBUS_MSGTYPE_C_STRIKEOUT: + return OBUS_PAYLDTYPE_GAMESTATUS; + + default: + return false; + break; + } + + // Module messages + } else { + switch (msg_type) { + case OBUS_MSGTYPE_M_STRIKE: + return OBUS_PAYLDTYPE_IDEMPOTENCY_ID; + + case OBUS_MSGTYPE_M_HELLO: + case OBUS_MSGTYPE_M_SOLVED: + return OBUS_PAYLDTYPE_EMPTY; + + default: + return -1; + break; + } + } +} + void obuscan_init() { obuscan_is_init = true; @@ -38,41 +73,9 @@ void obuscan_init() { } -void obuscan_send(struct obus_message *msg) { - if (!obuscan_is_init) { - Serial.println(F("Call obuscan_init first")); - return; - } - - struct can_frame send_frame; - - memset(&send_frame.data, 0, CAN_MAX_DLEN); - - uint8_t length = 1; - send_frame.data[0] = msg->msg_type; - - switch (msg->payload_type) { - case OBUS_PAYLDTYPE_EMPTY: break; - case OBUS_PAYLDTYPE_GAMESTATUS: - send_frame.data[1] = (uint8_t) ((msg->gamestatus.time_left & 0xFF000000) >> 0x18); - send_frame.data[2] = (uint8_t) ((msg->gamestatus.time_left & 0x00FF0000) >> 0x10); - send_frame.data[3] = (uint8_t) ((msg->gamestatus.time_left & 0x0000FF00) >> 0x08); - send_frame.data[4] = (uint8_t) (msg->gamestatus.time_left & 0x000000FF); - send_frame.data[5] = msg->gamestatus.strikes; - send_frame.data[6] = msg->gamestatus.max_strikes; - length = 7; - } - - send_frame.can_id = _encode_can_id(msg->from, msg->priority); - send_frame.can_dlc = length; - - mcp2515.sendMessage(&send_frame); -} - - bool obuscan_receive(struct obus_message *msg) { if (!obuscan_is_init) { - Serial.println(F("Call obuscan_init first")); + Serial.println(F("E Call obuscan_init first")); return false; } @@ -92,50 +95,13 @@ bool obuscan_receive(struct obus_message *msg) { } uint8_t msg_type = receive_frame.data[0]; - uint8_t payload_type = -1; - _decode_can_id(receive_frame.can_id, &msg->from, &msg->priority); + struct module from; + bool priority; + _decode_can_id(receive_frame.can_id, &from, &priority); + // Controller messages - // TODO ifdef, ignore not for us and assume for us - if (msg->from.type == OBUS_TYPE_CONTROLLER) { - switch (msg_type) { - case OBUS_MSGTYPE_C_ACK: // fall-through - case OBUS_MSGTYPE_C_HELLO: - payload_type = OBUS_PAYLDTYPE_EMPTY; - break; - - case OBUS_MSGTYPE_C_GAMESTART: // fall-through - case OBUS_MSGTYPE_C_STATE: - case OBUS_MSGTYPE_C_SOLVED: - case OBUS_MSGTYPE_C_TIMEOUT: - case OBUS_MSGTYPE_C_STRIKEOUT: - payload_type = OBUS_PAYLDTYPE_GAMESTATUS; - break; - - default: - return false; - break; - } - - // Module messages - } else { - switch (msg_type) { - case OBUS_MSGTYPE_M_STRIKE: - payload_type = OBUS_PAYLDTYPE_IDEMPOTENCY_ID; - break; - - case OBUS_MSGTYPE_M_HELLO: - case OBUS_MSGTYPE_M_SOLVED: - payload_type = OBUS_PAYLDTYPE_EMPTY; - break; - - default: - return false; - break; - } - } - - switch (payload_type) { + switch (obuscan_payload_type(from.type, msg_type)) { case OBUS_PAYLDTYPE_EMPTY: break; @@ -158,102 +124,52 @@ bool obuscan_receive(struct obus_message *msg) { break; default: - assert(false); - break; + Serial.println(F("W Couldn't determine payload type")); + return false; } + msg->from = from; + msg->priority = priority; msg->msg_type = msg_type; - msg->payload_type = payload_type; return true; } -inline struct obus_message _obuscan_msg( - struct module from, bool priority, uint8_t msg_type, uint8_t payload_type) { +void obuscan_send(struct obus_message *msg) { + if (!obuscan_is_init) { + Serial.println(F("E Call obuscan_init first")); + return; + } - struct obus_message msg; - msg.from = from; - msg.priority = priority; - msg.msg_type = msg_type; - msg.payload_type = payload_type; - return msg; -} - - -struct obus_message obuscan_msg_c_payld_gamestatus( - struct module from, bool priority, uint8_t msg_type, - uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - assert(from.type == OBUS_TYPE_CONTROLLER); - - struct obus_message msg = _obuscan_msg( - from, priority, msg_type, OBUS_PAYLDTYPE_GAMESTATUS); - msg.gamestatus.time_left = time_left; - msg.gamestatus.strikes = strikes; - msg.gamestatus.max_strikes = max_strikes; - return msg; -} - - -struct obus_message obuscan_msg_c_ack(struct module from) { - assert(from.type == OBUS_TYPE_CONTROLLER); - return _obuscan_msg(from, false, OBUS_MSGTYPE_C_ACK, OBUS_PAYLDTYPE_EMPTY); -} - -struct obus_message obuscan_msg_c_hello(struct module from) { - assert(from.type == OBUS_TYPE_CONTROLLER); - return _obuscan_msg(from, false, OBUS_MSGTYPE_C_HELLO, OBUS_PAYLDTYPE_EMPTY); -} - - -struct obus_message obuscan_msg_c_gamestart( - struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - return obuscan_msg_c_payld_gamestatus( - from, false, OBUS_MSGTYPE_C_GAMESTART, time_left, strikes, max_strikes); -} - -struct obus_message obuscan_msg_c_state( - struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - return obuscan_msg_c_payld_gamestatus( - from, false, OBUS_MSGTYPE_C_STATE, time_left, strikes, max_strikes); -} - -struct obus_message obuscan_msg_c_solved( - struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - return obuscan_msg_c_payld_gamestatus( - from, false, OBUS_MSGTYPE_C_SOLVED, time_left, strikes, max_strikes); -} - -struct obus_message obuscan_msg_c_timeout( - struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - return obuscan_msg_c_payld_gamestatus( - from, false, OBUS_MSGTYPE_C_TIMEOUT, time_left, strikes, max_strikes); -} - -struct obus_message obuscan_msg_c_strikeout( - struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { - - return obuscan_msg_c_payld_gamestatus( - from, false, OBUS_MSGTYPE_C_STRIKEOUT, time_left, strikes, max_strikes); -} - - -struct obus_message obuscan_msg_m_hello(struct module from) { - assert(from.type != OBUS_TYPE_CONTROLLER); - return _obuscan_msg(from, false, OBUS_MSGTYPE_M_HELLO, OBUS_PAYLDTYPE_EMPTY); -} - -struct obus_message obuscan_msg_m_strike(struct module from) { - assert(from.type != OBUS_TYPE_CONTROLLER); - return _obuscan_msg(from, false, OBUS_MSGTYPE_M_STRIKE, OBUS_PAYLDTYPE_EMPTY); -} - -struct obus_message obuscan_msg_m_solved(struct module from) { - assert(from.type != OBUS_TYPE_CONTROLLER); - return _obuscan_msg(from, false, OBUS_MSGTYPE_M_STRIKE, OBUS_PAYLDTYPE_EMPTY); + struct can_frame send_frame; + + memset(&send_frame.data, 0, CAN_MAX_DLEN); + + uint8_t length = 1; + send_frame.data[0] = msg->msg_type; + + switch (obuscan_payload_type(msg->from.type, msg->msg_type)) { + case OBUS_PAYLDTYPE_EMPTY: + break; + + case OBUS_PAYLDTYPE_GAMESTATUS: + send_frame.data[1] = (uint8_t) ((msg->gamestatus.time_left & 0xFF000000) >> 0x18); + send_frame.data[2] = (uint8_t) ((msg->gamestatus.time_left & 0x00FF0000) >> 0x10); + send_frame.data[3] = (uint8_t) ((msg->gamestatus.time_left & 0x0000FF00) >> 0x08); + send_frame.data[4] = (uint8_t) (msg->gamestatus.time_left & 0x000000FF); + send_frame.data[5] = msg->gamestatus.strikes; + send_frame.data[6] = msg->gamestatus.max_strikes; + length = 7; + break; + + default: + Serial.println(F("Unknown payload type")); + return; + } + + send_frame.can_id = _encode_can_id(msg->from, msg->priority); + send_frame.can_dlc = length; + + mcp2515.sendMessage(&send_frame); } diff --git a/src/controller/obus_can.hpp b/src/controller/obus_can.hpp index 7378919..22c4672 100644 --- a/src/controller/obus_can.hpp +++ b/src/controller/obus_can.hpp @@ -1,6 +1,8 @@ #ifndef OBUS_CAN_H #define OBUS_CAN_H +#include + #define CAN_DOMINANT 0 #define CAN_RECESSIVE 1 @@ -48,7 +50,6 @@ struct obus_message { struct module from; bool priority; uint8_t msg_type; - uint8_t payload_type; union { struct payld_empty empty; struct payld_gamestatus gamestatus; @@ -56,24 +57,172 @@ struct obus_message { }; }; + +/** + * Determine payload type for message + * + * @param module_type One of OBUS_TYPE_* + * @param module_type One of OBUS_MSGTYPE_* + * + * @return One of OBUS_PAYLDTYPE_* + */ +uint8_t obuscan_payload_type(uint8_t module_type, uint8_t message_type); + + +/** + * Initialize the CAN controller for OBUS messaging + */ void obuscan_init(); -void obuscan_send(struct obus_message *msg); +/** + * Receive a message + * + * @param msg Pointer to memory where the received message will be wriitten + * @return true if a message was received, false otherwise + */ bool obuscan_receive(struct obus_message *msg); -struct obus_message obuscan_msg_c_payld_gamestatus( +/** + * Lowlevel interface to send a message, you may want to use one of the helpers, like + * obuscan_send_m_strike + * + * @param msg Pointer to a message to send + */ +void obuscan_send(struct obus_message *msg); + + +/** + * For internal use only + * + * Send an OBUS message + */ +inline struct obus_message _obuscan_msg(struct module from, bool priority, uint8_t msg_type) { + + struct obus_message msg; + msg.from = from; + msg.priority = priority; + msg.msg_type = msg_type; + return msg; +} + + +/** + * For internal use only + * + * Send a controller OBUS message with a gamestatus payload + */ +inline void _obuscan_send_payld_gamestatus( struct module from, bool priority, uint8_t msg_type, - uint32_t time_left, uint8_t strikes, uint8_t max_strikes); + uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { -struct obus_message obuscan_msg_c_ack(struct module from); -struct obus_message obuscan_msg_c_hello(struct module from); -struct obus_message obuscan_msg_c_gamestart(struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes); -struct obus_message obuscan_msg_c_state(struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes); -struct obus_message obuscan_msg_c_solved(struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes); -struct obus_message obuscan_msg_c_timeout(struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes); -struct obus_message obuscan_msg_c_strikeout(struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes); + struct obus_message msg = _obuscan_msg(from, priority, msg_type); + msg.gamestatus.time_left = time_left; + msg.gamestatus.strikes = strikes; + msg.gamestatus.max_strikes = max_strikes; + obuscan_send(&msg); +} -struct obus_message obuscan_msg_m_hello(struct module from); -struct obus_message obuscan_msg_m_strike(struct module from); -struct obus_message obuscan_msg_m_solved(struct module from); + + +/** + * Send a controller "ACK" OBUS message + */ +inline void obuscan_send_c_ack(struct module from) { + assert(from.type == OBUS_TYPE_CONTROLLER); + struct obus_message msg = _obuscan_msg(from, false, OBUS_MSGTYPE_C_ACK); + obuscan_send(&msg); +} + +/** + * Send a controller "hello" OBUS message + */ +inline void obuscan_send_c_hello(struct module from) { + assert(from.type == OBUS_TYPE_CONTROLLER); + struct obus_message msg = _obuscan_msg(from, false, OBUS_MSGTYPE_C_HELLO); + obuscan_send(&msg); +} + + +/** + * Send a controller "game start" OBUS message + */ +inline void obuscan_send_c_gamestart( + struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { + + assert(from.type == OBUS_TYPE_CONTROLLER); + _obuscan_send_payld_gamestatus( + from, false, OBUS_MSGTYPE_C_GAMESTART, time_left, strikes, max_strikes); +} + +/** + * Send a controller "state" OBUS message + */ +inline void obuscan_send_c_state( + struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { + + assert(from.type == OBUS_TYPE_CONTROLLER); + _obuscan_send_payld_gamestatus( + from, false, OBUS_MSGTYPE_C_STATE, time_left, strikes, max_strikes); +} + +/** + * Send a controller "solved" OBUS message + */ +inline void obuscan_send_c_solved( + struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { + + assert(from.type == OBUS_TYPE_CONTROLLER); + _obuscan_send_payld_gamestatus( + from, false, OBUS_MSGTYPE_C_SOLVED, time_left, strikes, max_strikes); +} + +/** + * Send a controller "timeout" OBUS message + */ +inline void obuscan_send_c_timeout( + struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { + + assert(from.type == OBUS_TYPE_CONTROLLER); + _obuscan_send_payld_gamestatus( + from, false, OBUS_MSGTYPE_C_TIMEOUT, time_left, strikes, max_strikes); +} + +/** + * Send a controller "strikeout" OBUS message + */ +inline void obuscan_send_c_strikeout( + struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes) { + + assert(from.type == OBUS_TYPE_CONTROLLER); + _obuscan_send_payld_gamestatus( + from, false, OBUS_MSGTYPE_C_STRIKEOUT, time_left, strikes, max_strikes); +} + + +/** + * Send a module "hello" OBUS message + */ +inline void obuscan_send_m_hello(struct module from) { + assert(from.type != OBUS_TYPE_CONTROLLER); + struct obus_message msg = _obuscan_msg(from, false, OBUS_MSGTYPE_M_HELLO); + obuscan_send(&msg); +} + +/** + * Send a module "strike" OBUS message + */ +inline void obuscan_send_m_strike(struct module from) { + assert(from.type != OBUS_TYPE_CONTROLLER); + struct obus_message msg = _obuscan_msg(from, false, OBUS_MSGTYPE_M_STRIKE); + obuscan_send(&msg); +} + +/** + * Send a module "solved" OBUS message + */ +inline void obuscan_send_m_solved(struct module from) { + assert(from.type != OBUS_TYPE_CONTROLLER); + struct obus_message msg = _obuscan_msg(from, false, OBUS_MSGTYPE_M_STRIKE); + obuscan_send(&msg); +} #endif /* end of include guard: OBUS_CAN_H */