#ifndef OBUS_CAN_H #define OBUS_CAN_H #include "Arduino.h" #include #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_INFO 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_C_INFOSTART 7 #define OBUS_MSGTYPE_M_HELLO 0 #define OBUS_MSGTYPE_M_STRIKE 1 #define OBUS_MSGTYPE_M_SOLVED 2 #define OBUS_MSGTYPE_I_INFOMESSAGE 0 #define OBUS_PAYLDTYPE_EMPTY 0 #define OBUS_PAYLDTYPE_GAMESTATUS 1 #define OBUS_PAYLDTYPE_COUNT 2 #define OBUS_PAYLDTYPE_INFO 3 #define OBUS_PAYLDTYPE_MODULEADDR 4 #define OBUS_PAYLDTYPE_INFOSTART 5 #define OBUS_PAYLD_INFO_MAXLEN (OBUS_MSG_LENGTH - 1) namespace obus_can { 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; uint8_t puzzle_modules_left; }; struct payld_infomessage { uint8_t len; uint8_t data[OBUS_PAYLD_INFO_MAXLEN]; }; struct payld_infostart { uint32_t seed; }; struct message { struct module from; bool priority; uint8_t msg_type; union { struct payld_empty empty; struct payld_gamestatus gamestatus; uint8_t count; struct payld_infomessage infomessage; struct module payload_address; struct payld_infostart infostart; }; }; /** * 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 payload_type(uint8_t module_type, uint8_t message_type); /** * Initialize the CAN controller for OBUS messaging * * @return true if initialization and self-test succeeded, false otherwise */ bool init(); /** * Receive a message * * @param msg Pointer to memory where the received message will be written * @return true if a message was received, false otherwise */ bool receive(struct message *msg); /** * Lowlevel interface to send a message, you may want to use one of the helpers, like * send_m_strike * * @param msg Pointer to a message to send */ void send(struct message *msg); bool is_error_condition(); /** * For internal use only * * Send an OBUS message */ inline struct message _msg(struct module from, bool priority, uint8_t msg_type) { struct 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 _send_payld_gamestatus( struct module from, bool priority, uint8_t msg_type, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { struct message msg = _msg(from, priority, msg_type); msg.gamestatus.time_left = time_left; msg.gamestatus.strikes = strikes; msg.gamestatus.max_strikes = max_strikes; msg.gamestatus.puzzle_modules_left = puzzle_modules_left; send(&msg); } /** * Send a controller "ACK" OBUS message */ inline void send_c_ack(struct module from, struct module payload_address) { assert(from.type == OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_C_ACK); msg.payload_address = payload_address; send(&msg); } /** * Send a controller "hello" OBUS message */ inline void send_c_hello(struct module from) { assert(from.type == OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_C_HELLO); send(&msg); } /** * Send a controller "game start" OBUS message */ inline void send_c_gamestart( struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { assert(from.type == OBUS_TYPE_CONTROLLER); _send_payld_gamestatus( from, false, OBUS_MSGTYPE_C_GAMESTART, time_left, strikes, max_strikes, puzzle_modules_left); } /** * Send a controller "state" OBUS message */ inline void send_c_state( struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { assert(from.type == OBUS_TYPE_CONTROLLER); _send_payld_gamestatus( from, false, OBUS_MSGTYPE_C_STATE, time_left, strikes, max_strikes, puzzle_modules_left); } /** * Send a controller "solved" OBUS message */ inline void send_c_solved( struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { assert(from.type == OBUS_TYPE_CONTROLLER); _send_payld_gamestatus( from, false, OBUS_MSGTYPE_C_SOLVED, time_left, strikes, max_strikes, puzzle_modules_left); } /** * Send a controller "timeout" OBUS message */ inline void send_c_timeout( struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { assert(from.type == OBUS_TYPE_CONTROLLER); _send_payld_gamestatus( from, false, OBUS_MSGTYPE_C_TIMEOUT, time_left, strikes, max_strikes, puzzle_modules_left); } /** * Send a controller "info start" OBUS message */ inline void send_c_infostart(struct module from, uint32_t seed) { assert(from.type == OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_C_INFOSTART); msg.infostart.seed = seed; send(&msg); } /** * Send a controller "strikeout" OBUS message */ inline void send_c_strikeout( struct module from, uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) { assert(from.type == OBUS_TYPE_CONTROLLER); _send_payld_gamestatus( from, false, OBUS_MSGTYPE_C_STRIKEOUT, time_left, strikes, max_strikes, puzzle_modules_left); } /** * Send a module "hello" OBUS message */ inline void send_m_hello(struct module from) { assert(from.type != OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_M_HELLO); send(&msg); } /** * Send a module "strike" OBUS message */ inline void send_m_strike(struct module from, uint8_t count) { assert(from.type != OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_M_STRIKE); msg.count = count; send(&msg); } /** * Send a module "solved" OBUS message */ inline void send_m_solved(struct module from) { assert(from.type != OBUS_TYPE_CONTROLLER); struct message msg = _msg(from, false, OBUS_MSGTYPE_M_SOLVED); send(&msg); } /** * Send an info module "information" OBUS message */ inline void send_i_infomessage(struct module from, uint8_t *data, uint8_t data_len) { assert(from.type == OBUS_TYPE_INFO && from.id != OBUS_CONTROLLER_ID); if (data_len > OBUS_PAYLD_INFO_MAXLEN) { Serial.println(F("E Info payload too long")); return; } struct message msg = _msg(from, false, OBUS_MSGTYPE_I_INFOMESSAGE); msg.infomessage.len = data_len; memset(msg.infomessage.data, 0, OBUS_PAYLD_INFO_MAXLEN); memcpy(msg.infomessage.data, data, data_len); send(&msg); } inline bool is_from_controller(struct module from) { return from.type == OBUS_TYPE_CONTROLLER && from.id == OBUS_CONTROLLER_ID; } } #endif /* end of include guard: OBUS_CAN_H */