obus/src/controller/controller.ino

283 lines
6.6 KiB
Arduino
Raw Normal View History

2020-08-10 18:02:17 +02:00
#include <mcp2515.h>
2020-08-10 20:46:41 +02:00
#include <assert.h>
2020-08-10 18:02:17 +02:00
#define STATE_INACTIVE 0
#define STATE_HELLO 1
#define STATE_GAME 2
#define OBUS_CONTROLLER_ID 0x000
#define OBUS_TYPE_CONTROLLER 0
#define OBUS_TYPE_PUZZLE 1
#define OBUS_TYPE_NEEDY 2
#define OBUS_MSG_LENGTH 8 // Max 8 to fit in a CAN message
#define OBUS_MAX_MODULES 16
2020-08-10 23:49:49 +02:00
#define OBUS_DISC_DURATION 5 // Duration of discovery round in seconds
#define OBUS_GAME_DURATION 60 // Duration of the game in seconds
#define OBUS_MAX_STRIKEOUTS 3 // Number of strikeouts allowed until game over
2020-08-10 22:40:04 +02:00
#define OBUS_UPDATE_INTERVAL 500 // Number of milliseconds between game updates
2020-08-10 21:23:09 +02:00
2020-08-10 18:02:17 +02:00
#define OBUS_MSGTYPE_C_ACK 0
#define OBUS_MSGTYPE_C_HELLO 1
#define OBUS_MSGTYPE_C_GAMESTART 2
#define OBUS_MSGTYPE_C_STATE 3
#define OBUS_MSGTYPE_C_SOLVED 4
#define OBUS_MSGTYPE_C_TIMEOUT 5
#define OBUS_MSGTYPE_C_STRIKEOUT 6
#define OBUS_MSGTYPE_M_HELLO 0
#define OBUS_MSGTYPE_M_STRIKE 1
#define OBUS_MSGTYPE_M_SOLVED 2
#define CAN_DOMINANT 0
#define CAN_RECESSIVE 1
2020-08-10 22:40:04 +02:00
struct module {
2020-08-18 16:15:09 +02:00
uint8_t id;
uint8_t type;
2020-08-10 22:40:04 +02:00
};
2020-08-10 18:02:17 +02:00
MCP2515 mcp2515(10);
uint8_t state = STATE_INACTIVE;
2020-08-10 22:40:04 +02:00
struct module connected_modules_ids[OBUS_MAX_MODULES];
2020-08-10 21:23:09 +02:00
uint8_t nr_connected_modules;
2020-08-10 22:40:04 +02:00
uint8_t strikeouts;
uint8_t game_running;
2020-08-10 21:23:09 +02:00
// Bit vectors for checking if game is solved or not
uint8_t unsolved_puzzles[32]; // 256 bits
2020-08-10 22:40:04 +02:00
// TIMERS
uint16_t hello_round_start;
uint16_t game_start;
uint16_t last_update;
2020-08-10 18:02:17 +02:00
2020-08-10 18:02:17 +02:00
void setup() {
Serial.begin(9600);
mcp2515.reset();
mcp2515.setBitrate(CAN_50KBPS);
mcp2515.setNormalMode();
2020-08-10 21:23:09 +02:00
2020-08-18 16:15:09 +02:00
game_running = 0;
2020-08-10 18:02:17 +02:00
}
2020-08-10 18:02:17 +02:00
uint16_t make_id(uint8_t id, bool priority, uint8_t type) {
assert(type <= 0x11);
/* b bb bbbbbbbb
* type module ID
* priority bit
*/
return \
((uint16_t) (priority ? CAN_DOMINANT : CAN_RECESSIVE) << 10) | \
((uint16_t) type << 8) | \
(uint16_t) id;
}
2020-08-10 22:40:04 +02:00
struct module get_module_info(uint16_t can_id) {
2020-08-18 16:15:09 +02:00
uint8_t module_type = can_id & 0x0300;
uint8_t module_id = can_id & 0x00FF;
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
struct module module_info;
module_info.type = module_type;
module_info.id = module_id;
return module_info;
2020-08-10 22:40:04 +02:00
}
uint8_t check_solved() {
2020-08-18 16:15:09 +02:00
uint8_t solved = 1;
for (int i; i<32; i++) {
if (unsolved_puzzles != 0) {
solved = 0;
break;
}
}
return solved;
}
void add_module_to_bit_vector(uint8_t module_id) {
2020-08-18 16:15:09 +02:00
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) {
2020-08-18 16:15:09 +02:00
uint8_t byte_index = module_id >> 3;
uint8_t bit_index = module_id & 0x07;
unsolved_puzzles[byte_index] &= ~(0x1 << bit_index);
}
2020-08-10 23:49:49 +02:00
void send_message(uint8_t* message, uint8_t length) {
2020-08-18 16:15:09 +02:00
struct can_frame send_frame;
send_frame.can_id = make_id(OBUS_CONTROLLER_ID, false, OBUS_TYPE_CONTROLLER);
send_frame.can_dlc = length;
memcpy(send_frame.data, message, OBUS_MSG_LENGTH);
mcp2515.sendMessage(&send_frame);
2020-08-10 21:23:09 +02:00
}
2020-08-10 21:23:09 +02:00
void start_hello() {
state = STATE_HELLO;
2020-08-18 16:15:09 +02:00
hello_round_start = millis();
nr_connected_modules = 0;
// Zero bit vectors
for (int i; i<32; i++) {
unsolved_puzzles[i] = 0;
}
2020-08-10 21:23:09 +02:00
2020-08-18 16:15:09 +02:00
uint8_t message[OBUS_MSG_LENGTH];
message[0] = OBUS_MSGTYPE_C_HELLO;
2020-08-18 16:15:09 +02:00
send_message(message, 1);
2020-08-10 21:23:09 +02:00
2020-08-10 22:40:04 +02:00
Serial.println("Start of discovery round");
2020-08-10 18:02:17 +02:00
}
2020-08-10 21:23:09 +02:00
void send_ack() {
2020-08-18 16:15:09 +02:00
uint8_t message[OBUS_MSG_LENGTH];
message[0] = OBUS_MSGTYPE_C_ACK;
2020-08-10 21:23:09 +02:00
2020-08-18 16:15:09 +02:00
send_message(message, 1);
2020-08-10 21:23:09 +02:00
}
2020-08-10 20:46:41 +02:00
void receive_hello() {
2020-08-18 16:15:09 +02:00
struct can_frame receive_frame;
uint16_t current_time = millis();
if (mcp2515.readMessage(&receive_frame) == MCP2515::ERROR_OK) {
if (receive_frame.data[0] == OBUS_MSGTYPE_M_HELLO) {
struct module new_module = get_module_info(receive_frame.can_id);
Serial.print("Registered module ");
Serial.println(new_module.id);
connected_modules_ids[nr_connected_modules] = new_module;
nr_connected_modules++;
if (new_module.type == OBUS_TYPE_PUZZLE) {
add_module_to_bit_vector(new_module.id);
}
send_ack();
Serial.println("ACK");
}
} else if (current_time - hello_round_start > OBUS_DISC_DURATION * 1000) {
state = STATE_GAME;
Serial.println("End of discovery round");
}
2020-08-10 20:46:41 +02:00
}
2020-08-10 22:40:04 +02:00
void initialize_game() {
2020-08-18 16:15:09 +02:00
strikeouts = 0;
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
uint16_t game_duration_millis = (uint16_t) OBUS_GAME_DURATION * 1000;
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
uint8_t message[OBUS_MSG_LENGTH];
message[0] = OBUS_MSGTYPE_C_GAMESTART;
message[1] = (uint8_t) ((game_duration_millis & 0xFF000000) >> 0x18);
message[2] = (uint8_t) ((game_duration_millis & 0x00FF0000) >> 0x10);
message[3] = (uint8_t) ((game_duration_millis & 0x0000FF00) >> 0x08);
message[4] = (uint8_t) (game_duration_millis & 0x000000FF);
message[5] = strikeouts;
message[6] = OBUS_MAX_STRIKEOUTS;
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
send_message(message, 7);
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
game_running = 1;
game_start = millis();
last_update = game_start;
2020-08-10 22:40:04 +02:00
2020-08-18 16:15:09 +02:00
Serial.println("Game started");
2020-08-10 22:40:04 +02:00
}
2020-08-10 22:40:04 +02:00
void receive_module_update() {
2020-08-18 16:15:09 +02:00
struct can_frame receive_frame;
if (mcp2515.readMessage(&receive_frame) == MCP2515::ERROR_OK) {
if (receive_frame.data[0] == OBUS_MSGTYPE_M_STRIKE) {
strikeouts++;
} else if (receive_frame.data[0] == OBUS_MSGTYPE_M_SOLVED) {
struct module module_info = get_module_info(receive_frame.can_id);
solve_module_in_bit_vector(module_info.id);
}
}
2020-08-10 22:40:04 +02:00
}
void send_game_update(uint8_t status, uint16_t timestamp) {
2020-08-18 16:15:09 +02:00
uint8_t message[OBUS_MSG_LENGTH];
message[0] = status;
message[1] = (uint8_t) ((timestamp & 0xFF000000) >> 0x18);
message[2] = (uint8_t) ((timestamp & 0x00FF0000) >> 0x10);
message[3] = (uint8_t) ((timestamp & 0x0000FF00) >> 0x08);
message[4] = (uint8_t) (timestamp & 0x000000FF);
message[5] = strikeouts;
message[6] = OBUS_MAX_STRIKEOUTS;
send_message(message, 7);
}
2020-08-10 22:40:04 +02:00
void game_loop() {
2020-08-18 16:15:09 +02:00
uint16_t current_time = millis();
uint16_t game_duration = current_time - game_start;
receive_module_update();
if (check_solved()) {
Serial.println("Game solved");
send_game_update(OBUS_MSGTYPE_C_SOLVED, game_duration);
state = STATE_INACTIVE;
return;
} else if (game_duration >= (uint16_t) OBUS_GAME_DURATION * 1000) {
Serial.println("Times up");
send_game_update(OBUS_MSGTYPE_C_TIMEOUT, game_duration);
state = STATE_INACTIVE;
return;
} else if (strikeouts >= OBUS_MAX_STRIKEOUTS) {
Serial.println("Strikeout");
send_game_update(OBUS_MSGTYPE_C_STRIKEOUT, game_duration);
state = STATE_INACTIVE;
return;
}
uint16_t elapsed_time = current_time - last_update;
if (elapsed_time > OBUS_UPDATE_INTERVAL) {
Serial.print("Sending game update: ");
Serial.println(game_duration);
send_game_update(OBUS_MSGTYPE_C_STATE, (uint16_t) OBUS_GAME_DURATION * 1000 - game_duration);
last_update = current_time;
}
2020-08-10 22:40:04 +02:00
}
2020-08-10 18:02:17 +02:00
void loop() {
2020-08-18 16:15:09 +02:00
if (state == STATE_INACTIVE) {
start_hello();
} else if (state == STATE_HELLO) {
receive_hello();
} else if (state == STATE_GAME) {
if (game_running) {
game_loop();
} else {
initialize_game();
}
}
2020-08-10 18:02:17 +02:00
}