Finish an obuscan abstraction
This commit is contained in:
parent
71df9f71b9
commit
341ff1bb1e
5 changed files with 432 additions and 167 deletions
|
@ -1,42 +1,40 @@
|
||||||
#include <mcp2515.h>
|
#include "shared.hpp"
|
||||||
#include <assert.h>
|
#include "obus_can.hpp"
|
||||||
|
|
||||||
#include "../shared/shared.hpp"
|
|
||||||
|
|
||||||
#define STATE_INACTIVE 0
|
#define STATE_INACTIVE 0
|
||||||
#define STATE_HELLO 1
|
#define STATE_HELLO 1
|
||||||
#define STATE_GAME 2
|
#define STATE_GAME 2
|
||||||
|
|
||||||
#define OBUS_GAME_DURATION 60 // Duration of the game in seconds
|
#define OBUS_MAX_STRIKES 3 // Number of strikes allowed until game over
|
||||||
#define OBUS_MAX_STRIKEOUTS 3 // Number of strikeouts allowed until game over
|
#define OBUS_GAME_DURATION 10 // Duration of the game in seconds
|
||||||
|
|
||||||
|
|
||||||
MCP2515 mcp2515(10);
|
#define OBUS_GAME_DURATION_MS ((uint32_t) OBUS_GAME_DURATION*1000)
|
||||||
|
|
||||||
|
|
||||||
uint8_t state = STATE_INACTIVE;
|
uint8_t state = STATE_INACTIVE;
|
||||||
struct module connected_modules_ids[OBUS_MAX_MODULES];
|
struct module connected_modules_ids[OBUS_MAX_MODULES];
|
||||||
uint8_t nr_connected_modules;
|
uint8_t nr_connected_modules;
|
||||||
uint8_t strikeouts;
|
uint8_t strikes;
|
||||||
|
|
||||||
// Bit vectors for checking if game is solved or not
|
// Bit vectors for checking if game is solved or not
|
||||||
uint8_t unsolved_puzzles[32]; // 256 bits
|
uint8_t unsolved_puzzles[32]; // 256 bits
|
||||||
|
|
||||||
// TIMERS
|
// TIMERS
|
||||||
uint16_t hello_round_start;
|
uint32_t hello_round_start;
|
||||||
uint16_t game_start;
|
uint32_t game_start;
|
||||||
uint16_t last_update;
|
uint32_t last_update;
|
||||||
|
|
||||||
struct module this_module = (struct module) {
|
struct module this_module = {
|
||||||
.type = OBUS_TYPE_CONTROLLER;
|
.type = OBUS_TYPE_CONTROLLER,
|
||||||
.id = OBUS_CONTROLLER_ID;
|
.id = OBUS_CONTROLLER_ID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
mcp2515.reset();
|
obuscan_init();
|
||||||
mcp2515.setBitrate(CAN_50KBPS);
|
|
||||||
mcp2515.setNormalMode();
|
|
||||||
|
|
||||||
state = STATE_INACTIVE;
|
state = STATE_INACTIVE;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +42,7 @@ void setup() {
|
||||||
|
|
||||||
uint8_t check_solved() {
|
uint8_t check_solved() {
|
||||||
uint8_t solved = 1;
|
uint8_t solved = 1;
|
||||||
for (int i; i<32; i++) {
|
for (uint8_t i = 0; i < 32; i++) {
|
||||||
if (unsolved_puzzles != 0) {
|
if (unsolved_puzzles != 0) {
|
||||||
solved = 0;
|
solved = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -68,68 +66,48 @@ void solve_module_in_bit_vector(uint8_t module_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_message(uint8_t* message, uint8_t length) {
|
|
||||||
send_message(message, length, false);
|
|
||||||
}
|
|
||||||
void send_message(uint8_t* message, uint8_t length, bool priority) {
|
|
||||||
struct can_frame send_frame;
|
|
||||||
|
|
||||||
send_frame.can_id = encode_can_id(this_module, priority);
|
|
||||||
send_frame.can_dlc = length;
|
|
||||||
|
|
||||||
memcpy(send_frame.data, message, OBUS_MSG_LENGTH);
|
|
||||||
|
|
||||||
mcp2515.sendMessage(&send_frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void start_hello() {
|
void start_hello() {
|
||||||
state = STATE_HELLO;
|
state = STATE_HELLO;
|
||||||
hello_round_start = millis();
|
hello_round_start = millis();
|
||||||
nr_connected_modules = 0;
|
nr_connected_modules = 0;
|
||||||
|
|
||||||
// Zero bit vectors
|
// Zero bit vectors
|
||||||
for (int i; i<32; i++) {
|
for (uint8_t i = 0; i < 32; i++) {
|
||||||
unsolved_puzzles[i] = 0;
|
unsolved_puzzles[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t message[OBUS_MSG_LENGTH];
|
struct obus_message msg = obuscan_msg_c_hello(this_module);
|
||||||
message[0] = OBUS_MSGTYPE_C_HELLO;
|
obuscan_send(&msg);
|
||||||
|
|
||||||
send_message(message, 1);
|
Serial.println(F("Start of discovery round"));
|
||||||
|
|
||||||
Serial.println("Start of discovery round");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_ack() {
|
void send_ack() {
|
||||||
uint8_t message[OBUS_MSG_LENGTH];
|
struct obus_message msg = obuscan_msg_c_ack(this_module);
|
||||||
message[0] = OBUS_MSGTYPE_C_ACK;
|
obuscan_send(&msg);
|
||||||
|
|
||||||
send_message(message, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void receive_hello() {
|
void receive_hello() {
|
||||||
struct can_frame receive_frame;
|
struct obus_message msg;
|
||||||
uint16_t current_time = millis();
|
uint32_t current_time = millis();
|
||||||
|
|
||||||
if (mcp2515.readMessage(&receive_frame) == MCP2515::ERROR_OK) {
|
if (obuscan_receive(&msg)) {
|
||||||
if (receive_frame.data[0] == OBUS_MSGTYPE_M_HELLO) {
|
if (msg.msg_type == OBUS_MSGTYPE_M_HELLO) {
|
||||||
struct module new_module = decode_can_id(receive_frame.can_id);
|
|
||||||
Serial.print("Registered module ");
|
Serial.print("Registered module ");
|
||||||
Serial.println(full_module_id(new_module));
|
Serial.println(full_module_id(msg.from));
|
||||||
connected_modules_ids[nr_connected_modules] = new_module;
|
connected_modules_ids[nr_connected_modules] = msg.from;
|
||||||
nr_connected_modules++;
|
nr_connected_modules++;
|
||||||
|
|
||||||
if (new_module.type == OBUS_TYPE_PUZZLE) {
|
if (msg.from.type == OBUS_TYPE_PUZZLE) {
|
||||||
add_module_to_bit_vector(full_module_id(new_module));
|
add_module_to_bit_vector(full_module_id(msg.from));
|
||||||
}
|
}
|
||||||
|
|
||||||
send_ack();
|
send_ack();
|
||||||
Serial.println("ACK");
|
Serial.println("ACK");
|
||||||
}
|
}
|
||||||
} else if (current_time - hello_round_start > OBUS_DISC_DURATION * 1000) {
|
} 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();
|
initialize_game();
|
||||||
}
|
}
|
||||||
|
@ -137,86 +115,87 @@ void receive_hello() {
|
||||||
|
|
||||||
|
|
||||||
void initialize_game() {
|
void initialize_game() {
|
||||||
strikeouts = 0;
|
strikes = 0;
|
||||||
|
|
||||||
uint16_t game_duration_millis = (uint16_t) OBUS_GAME_DURATION * 1000;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
send_message(message, 7);
|
|
||||||
|
|
||||||
game_start = millis();
|
game_start = millis();
|
||||||
last_update = game_start;
|
|
||||||
|
|
||||||
|
last_update = game_start;
|
||||||
state = STATE_GAME;
|
state = STATE_GAME;
|
||||||
|
|
||||||
Serial.println("Game started");
|
Serial.println("Game started");
|
||||||
|
|
||||||
|
send_game_update(OBUS_MSGTYPE_C_GAMESTART, OBUS_GAME_DURATION_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void receive_module_update() {
|
void receive_module_update() {
|
||||||
struct can_frame receive_frame;
|
struct obus_message msg;
|
||||||
|
|
||||||
if (mcp2515.readMessage(&receive_frame) == MCP2515::ERROR_OK) {
|
if (obuscan_receive(&msg)) {
|
||||||
if (receive_frame.data[0] == OBUS_MSGTYPE_M_STRIKE) {
|
|
||||||
strikeouts++;
|
switch (msg.msg_type) {
|
||||||
} else if (receive_frame.data[0] == OBUS_MSGTYPE_M_SOLVED) {
|
case OBUS_MSGTYPE_M_STRIKE:
|
||||||
uint16_t module_id = full_module_id(decode_can_id(receive_frame.can_id));
|
// TODO check idempotency ID
|
||||||
solve_module_in_bit_vector(module_id);
|
strikes++;
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
case OBUS_MSGTYPE_M_SOLVED:
|
||||||
|
solve_module_in_bit_vector(full_module_id(msg.from));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Serial.print(F("W Ignoring msg "));
|
||||||
|
Serial.println(msg.msg_type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void send_game_update(uint8_t status, uint16_t timestamp) {
|
|
||||||
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);
|
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() {
|
void game_loop() {
|
||||||
uint16_t current_time = millis();
|
uint32_t current_time = millis();
|
||||||
uint16_t game_duration = current_time - game_start;
|
uint32_t time_elapsed = current_time - game_start;
|
||||||
|
uint32_t time_left =
|
||||||
|
OBUS_GAME_DURATION_MS < time_elapsed ? 0 : OBUS_GAME_DURATION_MS - time_elapsed;
|
||||||
|
// We cannot check for '<= 0' in an uint type so we check the terms prior to subtraction
|
||||||
|
|
||||||
receive_module_update();
|
receive_module_update();
|
||||||
|
|
||||||
if (check_solved()) {
|
if (check_solved()) {
|
||||||
Serial.println("Game solved");
|
Serial.println("Game solved");
|
||||||
send_game_update(OBUS_MSGTYPE_C_SOLVED, game_duration);
|
send_game_update(OBUS_MSGTYPE_C_SOLVED, time_left);
|
||||||
state = STATE_INACTIVE;
|
state = STATE_INACTIVE;
|
||||||
return;
|
return;
|
||||||
} else if (game_duration >= (uint16_t) OBUS_GAME_DURATION * 1000) {
|
} else if (time_left == 0) {
|
||||||
Serial.println("Time's up");
|
Serial.println("Time's up");
|
||||||
send_game_update(OBUS_MSGTYPE_C_TIMEOUT, game_duration);
|
send_game_update(OBUS_MSGTYPE_C_TIMEOUT, time_left);
|
||||||
state = STATE_INACTIVE;
|
state = STATE_INACTIVE;
|
||||||
return;
|
return;
|
||||||
} else if (strikeouts >= OBUS_MAX_STRIKEOUTS) {
|
} else if (strikes >= OBUS_MAX_STRIKES) {
|
||||||
Serial.println("Strikeout");
|
Serial.println("Strikeout");
|
||||||
send_game_update(OBUS_MSGTYPE_C_STRIKEOUT, game_duration);
|
send_game_update(OBUS_MSGTYPE_C_STRIKEOUT, time_left);
|
||||||
state = STATE_INACTIVE;
|
state = STATE_INACTIVE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t elapsed_time = current_time - last_update;
|
if (last_update + OBUS_UPDATE_INTERVAL <= current_time) {
|
||||||
if (elapsed_time > OBUS_UPDATE_INTERVAL) {
|
send_game_update(OBUS_MSGTYPE_C_STATE, time_left);
|
||||||
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;
|
last_update = current_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
259
src/controller/obus_can.cpp
Normal file
259
src/controller/obus_can.cpp
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
#include <mcp2515.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "obus_can.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
MCP2515 mcp2515(10);
|
||||||
|
bool obuscan_is_init = false;
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t _encode_can_id(struct module mod, bool priority) {
|
||||||
|
assert(mod.type <= 0b11);
|
||||||
|
|
||||||
|
/* b bb bbbbbbbb
|
||||||
|
* ↓ type module ID
|
||||||
|
* priority bit
|
||||||
|
*/
|
||||||
|
return \
|
||||||
|
((uint16_t) (priority ? CAN_DOMINANT : CAN_RECESSIVE) << 10) | \
|
||||||
|
((uint16_t) mod.type << 8) | \
|
||||||
|
(uint16_t) mod.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _decode_can_id(uint16_t can_id, struct module *mod, bool *priority) {
|
||||||
|
*priority = ((can_id >> 10) & 1) == CAN_DOMINANT;
|
||||||
|
mod->type = (can_id >> 8) & 0b11;
|
||||||
|
mod->id = can_id & 0b11111111;
|
||||||
|
|
||||||
|
assert(mod->type <= 0b11);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void obuscan_init() {
|
||||||
|
obuscan_is_init = true;
|
||||||
|
mcp2515.reset();
|
||||||
|
mcp2515.setBitrate(CAN_50KBPS);
|
||||||
|
mcp2515.setNormalMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct can_frame receive_frame;
|
||||||
|
|
||||||
|
memset(&receive_frame.data, 0, CAN_MAX_DLEN);
|
||||||
|
|
||||||
|
MCP2515::ERROR status = mcp2515.readMessage(&receive_frame);
|
||||||
|
if (status != MCP2515::ERROR_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always at least OBUS message type required
|
||||||
|
if (receive_frame.can_dlc < 1) {
|
||||||
|
Serial.println(F("W Received illegal msg: payload <1"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t msg_type = receive_frame.data[0];
|
||||||
|
uint8_t payload_type = -1;
|
||||||
|
|
||||||
|
_decode_can_id(receive_frame.can_id, &msg->from, &msg->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) {
|
||||||
|
case OBUS_PAYLDTYPE_EMPTY:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBUS_PAYLDTYPE_GAMESTATUS:
|
||||||
|
if (receive_frame.can_dlc < 7) {
|
||||||
|
Serial.println(F("W Received illegal gamestatus msg: payload <7"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
msg->gamestatus.time_left =
|
||||||
|
((uint32_t) receive_frame.data[1] << 0x18) |
|
||||||
|
((uint32_t) receive_frame.data[2] << 0x10) |
|
||||||
|
((uint32_t) receive_frame.data[3] << 0x08) |
|
||||||
|
((uint32_t) receive_frame.data[4]);
|
||||||
|
msg->gamestatus.strikes = receive_frame.data[5];
|
||||||
|
msg->gamestatus.max_strikes = receive_frame.data[6];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBUS_PAYLDTYPE_IDEMPOTENCY_ID:
|
||||||
|
msg->idempotency.id = receive_frame.data[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
79
src/controller/obus_can.hpp
Normal file
79
src/controller/obus_can.hpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef OBUS_CAN_H
|
||||||
|
#define OBUS_CAN_H
|
||||||
|
|
||||||
|
#define CAN_DOMINANT 0
|
||||||
|
#define CAN_RECESSIVE 1
|
||||||
|
|
||||||
|
#define OBUS_CONTROLLER_ID 0x000
|
||||||
|
|
||||||
|
#define OBUS_MSG_LENGTH 8 // Max 8 to fit in a CAN message
|
||||||
|
|
||||||
|
#define OBUS_TYPE_CONTROLLER 0
|
||||||
|
#define OBUS_TYPE_PUZZLE 1
|
||||||
|
#define OBUS_TYPE_NEEDY 2
|
||||||
|
|
||||||
|
#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 OBUS_PAYLDTYPE_EMPTY 0
|
||||||
|
#define OBUS_PAYLDTYPE_GAMESTATUS 1
|
||||||
|
#define OBUS_PAYLDTYPE_IDEMPOTENCY_ID 2
|
||||||
|
|
||||||
|
struct module {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct payld_empty {};
|
||||||
|
struct payld_gamestatus {
|
||||||
|
uint32_t time_left;
|
||||||
|
uint8_t strikes;
|
||||||
|
uint8_t max_strikes;
|
||||||
|
};
|
||||||
|
struct payld_idempotency {
|
||||||
|
uint8_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
struct payld_idempotency idempotency;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void obuscan_init();
|
||||||
|
void obuscan_send(struct obus_message *msg);
|
||||||
|
bool obuscan_receive(struct obus_message *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);
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
#endif /* end of include guard: OBUS_CAN_H */
|
18
src/controller/shared.hpp
Normal file
18
src/controller/shared.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef OBUS_SHARED_H
|
||||||
|
#define OBUS_SHARED_H
|
||||||
|
|
||||||
|
#include "obus_can.hpp"
|
||||||
|
|
||||||
|
#define OBUS_MAX_MODULES 16
|
||||||
|
#define OBUS_DISC_DURATION 5 // Duration of discovery round in seconds
|
||||||
|
#define OBUS_UPDATE_INTERVAL 500 // Number of milliseconds between game updates
|
||||||
|
|
||||||
|
#define OBUS_DISC_DURATION_MS ((uint32_t) OBUS_DISC_DURATION*1000)
|
||||||
|
|
||||||
|
uint16_t full_module_id(struct module mod) {
|
||||||
|
return \
|
||||||
|
((uint16_t) mod.type << 8) | \
|
||||||
|
(uint16_t) mod.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* end of include guard: OBUS_DEFS_H */
|
|
@ -1,70 +0,0 @@
|
||||||
#ifndef OBUS_SHARED_H
|
|
||||||
#define OBUS_SHARED_H
|
|
||||||
|
|
||||||
#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
|
|
||||||
#define OBUS_DISC_DURATION 5 // Duration of discovery round in seconds
|
|
||||||
#define OBUS_UPDATE_INTERVAL 500 // Number of milliseconds between game updates
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
#define OBUS_MASK_PRIORITY = 0b10000000000
|
|
||||||
#define OBUS_MASK_TYPE = 0b01100000000
|
|
||||||
#define OBUS_MASK_ID = 0b00011111111
|
|
||||||
|
|
||||||
struct module {
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t id;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
uint16_t encode_can_id(struct module mod, bool priority) {
|
|
||||||
assert(mod.type <= 0b11);
|
|
||||||
|
|
||||||
/* b bb bbbbbbbb
|
|
||||||
* ↓ type module ID
|
|
||||||
* priority bit
|
|
||||||
*/
|
|
||||||
return \
|
|
||||||
((uint16_t) (priority ? CAN_DOMINANT : CAN_RECESSIVE) << 10) | \
|
|
||||||
((uint16_t) mod.type << 8) | \
|
|
||||||
(uint16_t) mod.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t full_module_id(struct module mod) {
|
|
||||||
return \
|
|
||||||
((uint16_t) mod.type << 8) | \
|
|
||||||
(uint16_t) mod.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct module decode_can_id(uint16_t can_id) {
|
|
||||||
struct module mod;
|
|
||||||
mod.type = (can_id & OBUS_MASK_TYPE) >> 8;
|
|
||||||
mod.id = can_id & OBUS_MASK_ID;
|
|
||||||
|
|
||||||
assert(mod.type <= 0x11);
|
|
||||||
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* end of include guard: OBUS_DEFS_H */
|
|
Loading…
Reference in a new issue