#include "obus_can.h" #include "obus_module.h" #define RED_LED A4 #define GREEN_LED A5 #define BLINK_DELAY_SLOW 1000 #define BLINK_DELAY_NORMAL 500 #define BLINK_DELAY_FAST 300 #define MCP_INT 2 #define COLOR_OFF ((struct color) {false, false}) #define COLOR_RED ((struct color) {true, false}) #define COLOR_GREEN ((struct color) {false, true}) #define COLOR_YELLOW ((struct color) {true, true}) namespace obus_module { struct obus_can::module this_module; uint8_t strike_count; bool active; uint32_t time_stop_strike_led; // Current LED status struct color { bool red; bool green; }; struct color led_color; bool blink_on = false; int blink_delay = 0; unsigned long blink_next_time = 0; // Update blink of status LED, to be called in some loop void _updateLed() { if (blink_delay && millis() > blink_next_time) { blink_on = !blink_on; if (blink_on) { digitalWrite(RED_LED, led_color.red ? HIGH : LOW); digitalWrite(GREEN_LED, led_color.green ? HIGH : LOW); } else { digitalWrite(RED_LED, false); digitalWrite(GREEN_LED, false); } blink_next_time = millis() + blink_delay; } } void _setLed(struct color color) { led_color = color; blink_delay = 0; digitalWrite(RED_LED, color.red ? HIGH : LOW); digitalWrite(GREEN_LED, color.green ? HIGH : LOW); } void _setLedBlink(struct color color, uint16_t delay) { led_color = color; blink_on = false; blink_delay = delay; blink_next_time = millis(); _updateLed(); } void setup(uint8_t type, uint8_t id) { this_module.type = type; this_module.id = id; obus_can::init(); strike_count = 0; active = true; if (type == OBUS_TYPE_PUZZLE || type == OBUS_TYPE_NEEDY) { pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); _setLedBlink(COLOR_GREEN, BLINK_DELAY_SLOW); } } bool loopPuzzle(obus_can::message* message, void (*callback_game_start)(), void (*callback_game_stop)()) { // Check if we need to turn the red "strike" LED back off after // turning it on because of a strike if (time_stop_strike_led && millis() > time_stop_strike_led) { digitalWrite(RED_LED, LOW); time_stop_strike_led = 0; } // TODO this can be more efficient by only enabling error interrupts and // reacting to the interrupt instead of checking if the flag is set in a loop // We will need to fork our CAN library for this, because the needed functions // are private if (obus_can::is_error_condition()) { bool blink = false; while (true) { digitalWrite(RED_LED, blink); digitalWrite(GREEN_LED, blink); blink = !blink; delay(BLINK_DELAY_NORMAL); } } bool interesting_message = false; if (obus_can::receive(message)) { if (message->from.type == OBUS_TYPE_CONTROLLER && message->from.id == 0) { switch (message->msg_type) { case OBUS_MSGTYPE_C_GAMESTART: active = true; _setLed(COLOR_OFF); callback_game_start(); break; case OBUS_MSGTYPE_C_HELLO: obus_can::send_m_hello(this_module); break; case OBUS_MSGTYPE_C_SOLVED: case OBUS_MSGTYPE_C_TIMEOUT: case OBUS_MSGTYPE_C_STRIKEOUT: active = false; callback_game_stop(); break; case OBUS_MSGTYPE_C_ACK: break; case OBUS_MSGTYPE_C_STATE: interesting_message = true; break; default: break; } } } _updateLed(); return interesting_message; } bool loopNeedy(obus_can::message* message, void (*callback_game_start)(), void (*callback_game_stop)()) { // For now this is the same function return loopPuzzle(message, callback_game_start, callback_game_stop); } bool loopInfo(obus_can::message* message, int (*info_generator)(char*)) { bool interesting_message = false; if (obus_can::receive(message)) { if (message->from.type == OBUS_TYPE_CONTROLLER && message->from.id == 0) { switch (message->msg_type) { case OBUS_MSGTYPE_C_INFOSTART: char info_message[8]; int len = info_generator(info_message); obus_can::send_i_infomessage(this_module, info_message); break; case OBUS_MSGTYPE_C_STATE: interesting_message = true; break; default: break; } } } return interesting_message; } void strike() { if (!active) { return; } strike_count++; digitalWrite(RED_LED, HIGH); time_stop_strike_led = millis() + 2000; obus_can::send_m_strike(this_module, strike_count); } void solve() { if (!active) { return; } obus_can::send_m_solved(this_module); digitalWrite(GREEN_LED, HIGH); active = false; } bool is_active() { return active; } }