2020-08-26 21:13:16 +02:00
|
|
|
#include "obus_can.h"
|
|
|
|
#include "obus_module.h"
|
|
|
|
|
2021-02-01 19:18:28 +01:00
|
|
|
#define PIN_LED_RED 4
|
|
|
|
#define PIN_LED_GREEN 7
|
2020-08-26 23:35:13 +02:00
|
|
|
|
2020-09-07 18:21:51 +02:00
|
|
|
#define BLINK_DELAY_SLOW 1000
|
|
|
|
#define BLINK_DELAY_FAST 300
|
|
|
|
|
2021-02-01 17:00:15 +01:00
|
|
|
#define MAX_TIME_BETWEEN_CALLS 100
|
|
|
|
|
2020-09-07 18:21:51 +02:00
|
|
|
#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})
|
2020-08-26 21:13:16 +02:00
|
|
|
|
2020-09-07 18:21:51 +02:00
|
|
|
namespace obus_module {
|
2020-08-26 23:35:13 +02:00
|
|
|
|
2020-08-26 21:13:16 +02:00
|
|
|
struct obus_can::module this_module;
|
|
|
|
uint8_t strike_count;
|
2020-08-27 05:30:22 +02:00
|
|
|
bool active;
|
2022-01-25 21:16:29 +01:00
|
|
|
bool acked_after_last_hello = false;
|
2021-02-01 17:00:15 +01:00
|
|
|
uint32_t next_loop_call_deadline;
|
2020-08-26 21:13:16 +02:00
|
|
|
|
2020-09-07 18:21:51 +02:00
|
|
|
// Current LED status
|
|
|
|
struct color { bool red; bool green; };
|
|
|
|
struct color led_color;
|
2020-09-09 18:30:44 +02:00
|
|
|
// Keeps track of whether the LED is currently lit, when a blink pattern is active
|
|
|
|
bool blink_led_lit = false;
|
2020-09-07 18:21:51 +02:00
|
|
|
int blink_delay = 0;
|
|
|
|
unsigned long blink_next_time = 0;
|
2020-09-09 18:30:44 +02:00
|
|
|
uint32_t led_reset_time;
|
2020-09-07 18:21:51 +02:00
|
|
|
|
|
|
|
|
2020-09-09 18:30:44 +02:00
|
|
|
void _setLed(struct color color) {
|
|
|
|
led_color = color;
|
|
|
|
blink_delay = 0;
|
|
|
|
led_reset_time = 0;
|
|
|
|
|
2021-02-01 19:18:28 +01:00
|
|
|
digitalWrite(PIN_LED_RED, color.red ? HIGH : LOW);
|
|
|
|
digitalWrite(PIN_LED_GREEN, color.green ? HIGH : LOW);
|
2020-09-09 18:30:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void _ledLoop() {
|
|
|
|
// Check if we need to turn the LED back off, e.g. to reset the strike blinker
|
|
|
|
if (led_reset_time && millis() > led_reset_time) {
|
2020-09-09 20:00:05 +02:00
|
|
|
if (active) {
|
|
|
|
_setLed(COLOR_YELLOW);
|
|
|
|
} else {
|
|
|
|
_setLed(COLOR_OFF);
|
|
|
|
}
|
2020-09-09 18:30:44 +02:00
|
|
|
led_reset_time = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update blink of status LED
|
2020-09-07 18:21:51 +02:00
|
|
|
if (blink_delay && millis() > blink_next_time) {
|
2020-09-09 18:30:44 +02:00
|
|
|
blink_led_lit = !blink_led_lit;
|
|
|
|
if (blink_led_lit) {
|
2021-02-01 19:18:28 +01:00
|
|
|
digitalWrite(PIN_LED_RED, led_color.red ? HIGH : LOW);
|
|
|
|
digitalWrite(PIN_LED_GREEN, led_color.green ? HIGH : LOW);
|
2020-09-07 18:21:51 +02:00
|
|
|
} else {
|
2021-02-01 19:18:28 +01:00
|
|
|
digitalWrite(PIN_LED_RED, false);
|
|
|
|
digitalWrite(PIN_LED_GREEN, false);
|
2020-09-07 18:21:51 +02:00
|
|
|
}
|
|
|
|
|
2020-09-08 00:08:13 +02:00
|
|
|
blink_next_time = millis() + blink_delay;
|
2020-09-07 18:21:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _setLedBlink(struct color color, uint16_t delay) {
|
|
|
|
led_color = color;
|
2020-09-09 18:30:44 +02:00
|
|
|
blink_led_lit = false;
|
2020-09-07 18:21:51 +02:00
|
|
|
blink_delay = delay;
|
|
|
|
blink_next_time = millis();
|
2020-09-09 18:30:44 +02:00
|
|
|
led_reset_time = 0;
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2020-09-09 18:30:44 +02:00
|
|
|
_ledLoop();
|
2020-09-07 18:21:51 +02:00
|
|
|
}
|
|
|
|
|
2021-07-31 22:21:48 +02:00
|
|
|
void blink_error(String message) {
|
|
|
|
bool blink = false;
|
|
|
|
while (true) {
|
|
|
|
digitalWrite(PIN_LED_RED, blink);
|
|
|
|
digitalWrite(PIN_LED_GREEN, blink);
|
|
|
|
blink = !blink;
|
|
|
|
delay(blink ? BLINK_DELAY_SLOW : BLINK_DELAY_FAST);
|
|
|
|
Serial.println(message);
|
|
|
|
}
|
|
|
|
}
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2020-09-09 22:00:59 +02:00
|
|
|
void _resetState() {
|
2020-08-26 21:13:16 +02:00
|
|
|
strike_count = 0;
|
2020-09-09 22:00:59 +02:00
|
|
|
active = false;
|
2021-02-01 17:00:15 +01:00
|
|
|
next_loop_call_deadline = 0;
|
2022-01-25 21:16:29 +01:00
|
|
|
acked_after_last_hello = false;
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2020-09-09 22:00:59 +02:00
|
|
|
if (this_module.type == OBUS_TYPE_PUZZLE || this_module.type == OBUS_TYPE_NEEDY) {
|
2021-02-01 19:18:28 +01:00
|
|
|
pinMode(PIN_LED_RED, OUTPUT);
|
|
|
|
pinMode(PIN_LED_GREEN, OUTPUT);
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2020-09-09 18:18:55 +02:00
|
|
|
_setLedBlink(COLOR_GREEN, BLINK_DELAY_SLOW);
|
|
|
|
}
|
2020-08-26 21:13:16 +02:00
|
|
|
}
|
|
|
|
|
2020-09-09 22:00:59 +02:00
|
|
|
void setup(uint8_t type, uint8_t id) {
|
|
|
|
this_module.type = type;
|
|
|
|
this_module.id = id;
|
|
|
|
_resetState();
|
2021-07-31 22:21:48 +02:00
|
|
|
|
|
|
|
if (!obus_can::init()) {
|
|
|
|
blink_error(F("CAN init failed"));
|
|
|
|
}
|
2020-09-09 22:00:59 +02:00
|
|
|
}
|
|
|
|
|
2021-02-01 15:33:14 +01:00
|
|
|
void empty_callback_info(uint8_t info_id, uint8_t infomessage[7]) {
|
|
|
|
// Mark arguments as not used
|
|
|
|
(void)info_id;
|
|
|
|
(void)infomessage;
|
|
|
|
}
|
|
|
|
|
2021-02-01 18:51:02 +01:00
|
|
|
void empty_callback_state(uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left) {
|
2021-02-01 15:33:14 +01:00
|
|
|
// Mark arguments as not used
|
|
|
|
(void)time_left;
|
|
|
|
(void)strikes;
|
|
|
|
(void)max_strikes;
|
2021-02-01 18:51:02 +01:00
|
|
|
(void)puzzle_modules_left;
|
2021-02-01 15:33:14 +01:00
|
|
|
}
|
|
|
|
|
2021-02-01 18:51:02 +01:00
|
|
|
bool loopPuzzle(obus_can::message* message, void (*callback_game_start)(uint8_t puzzle_modules), void (*callback_game_stop)(), void (*callback_info)(uint8_t info_id, uint8_t infomessage[7]), void (*callback_state)(uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left)) {
|
2020-08-27 03:29:16 +02:00
|
|
|
// 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
|
2021-01-30 13:51:43 +01:00
|
|
|
// We will need to fork our CAN library for this, because the needed functions are private.
|
|
|
|
// Also, we can't do this by default, because the INT pin is normally not connected to the board
|
2020-08-27 03:29:16 +02:00
|
|
|
if (obus_can::is_error_condition()) {
|
2021-02-01 17:00:15 +01:00
|
|
|
blink_error(F("E CAN error"));
|
2020-08-26 23:35:13 +02:00
|
|
|
}
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2021-02-01 18:13:23 +01:00
|
|
|
// Force the user of the library to periodically call loop
|
2021-02-01 17:00:15 +01:00
|
|
|
if (next_loop_call_deadline != 0 && millis() > next_loop_call_deadline) {
|
2021-02-01 18:13:23 +01:00
|
|
|
blink_error(F("E Missed loop deadline"));
|
2020-08-26 23:35:13 +02:00
|
|
|
}
|
2021-02-01 17:00:15 +01:00
|
|
|
next_loop_call_deadline = millis() + MAX_TIME_BETWEEN_CALLS;
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2021-02-01 15:33:14 +01:00
|
|
|
bool received_message = false;
|
2020-08-27 04:02:10 +02:00
|
|
|
if (obus_can::receive(message)) {
|
2021-02-01 15:33:14 +01:00
|
|
|
received_message = true;
|
2020-10-22 21:16:12 +02:00
|
|
|
if (is_from_controller(message->from)) {
|
2022-02-10 17:43:45 +01:00
|
|
|
uint32_t seed;
|
2020-09-09 18:18:55 +02:00
|
|
|
switch (message->msg_type) {
|
|
|
|
case OBUS_MSGTYPE_C_GAMESTART:
|
2022-01-25 21:16:29 +01:00
|
|
|
if (acked_after_last_hello) {
|
|
|
|
active = true;
|
|
|
|
_setLed(COLOR_YELLOW);
|
|
|
|
callback_game_start(message->gamestatus.puzzle_modules_left);
|
|
|
|
}
|
2020-09-09 18:18:55 +02:00
|
|
|
break;
|
|
|
|
case OBUS_MSGTYPE_C_HELLO:
|
2020-09-09 22:00:59 +02:00
|
|
|
_resetState();
|
2020-09-09 18:18:55 +02:00
|
|
|
obus_can::send_m_hello(this_module);
|
|
|
|
break;
|
|
|
|
case OBUS_MSGTYPE_C_SOLVED:
|
|
|
|
case OBUS_MSGTYPE_C_TIMEOUT:
|
|
|
|
case OBUS_MSGTYPE_C_STRIKEOUT:
|
2022-01-25 21:16:29 +01:00
|
|
|
if (acked_after_last_hello) {
|
|
|
|
active = false;
|
|
|
|
_setLed(COLOR_OFF);
|
|
|
|
callback_game_stop();
|
|
|
|
}
|
2020-09-09 18:18:55 +02:00
|
|
|
break;
|
|
|
|
case OBUS_MSGTYPE_C_ACK:
|
2022-01-25 21:16:29 +01:00
|
|
|
if (message->payload_address.type == this_module.type && message->payload_address.id == this_module.id) {
|
|
|
|
acked_after_last_hello = true;
|
|
|
|
}
|
2020-09-09 18:18:55 +02:00
|
|
|
break;
|
|
|
|
case OBUS_MSGTYPE_C_STATE:
|
2021-02-01 18:51:02 +01:00
|
|
|
callback_state(message->gamestatus.time_left, message->gamestatus.strikes, message->gamestatus.max_strikes, message->gamestatus.puzzle_modules_left);
|
2020-09-09 18:18:55 +02:00
|
|
|
break;
|
2021-02-03 01:25:03 +01:00
|
|
|
case OBUS_MSGTYPE_C_INFOSTART:
|
2022-01-24 22:50:13 +01:00
|
|
|
// Add module type and id to seed, to remove correlation in randomness between modules
|
2022-02-10 17:43:45 +01:00
|
|
|
seed = message->infostart.seed + ((uint32_t) this_module.type << 8) + ((uint32_t) this_module.id);
|
2022-01-24 22:50:13 +01:00
|
|
|
// randomSeed has no effect when called with 0 as seed, so we use
|
2022-01-19 21:33:41 +01:00
|
|
|
// a fallback value that is unlikely to collide with other frequently used seeds
|
2022-01-24 22:50:13 +01:00
|
|
|
if (seed == 0) {
|
|
|
|
seed--;
|
|
|
|
}
|
|
|
|
randomSeed(seed);
|
2021-02-03 01:25:03 +01:00
|
|
|
break;
|
2020-09-09 18:18:55 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-01-30 13:51:43 +01:00
|
|
|
} else if (message->from.type == OBUS_TYPE_INFO) {
|
|
|
|
uint8_t infobuffer[7] = {0};
|
|
|
|
memcpy(infobuffer, message->infomessage.data, message->infomessage.len);
|
|
|
|
callback_info(message->from.id, infobuffer);
|
2020-08-27 03:29:16 +02:00
|
|
|
}
|
2020-08-26 23:35:13 +02:00
|
|
|
}
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2020-09-09 18:30:44 +02:00
|
|
|
_ledLoop();
|
2020-09-07 18:21:51 +02:00
|
|
|
|
2021-02-01 15:33:14 +01:00
|
|
|
return received_message;
|
2020-08-27 05:30:22 +02:00
|
|
|
}
|
2020-08-26 21:13:16 +02:00
|
|
|
|
2021-02-01 18:51:02 +01:00
|
|
|
bool loopNeedy(obus_can::message* message, void (*callback_game_start)(uint8_t puzzle_modules), void (*callback_game_stop)(), void (*callback_info)(uint8_t info_id, uint8_t infomessage[7]), void (*callback_state)(uint32_t time_left, uint8_t strikes, uint8_t max_strikes, uint8_t puzzle_modules_left)) {
|
2020-08-27 05:30:22 +02:00
|
|
|
// For now this is the same function
|
2021-02-01 15:33:14 +01:00
|
|
|
return loopPuzzle(message, callback_game_start, callback_game_stop, callback_info, callback_state);
|
2020-09-09 18:18:55 +02:00
|
|
|
}
|
|
|
|
|
2020-09-09 22:00:59 +02:00
|
|
|
bool loopInfo(obus_can::message* message, int (*info_generator)(uint8_t*)) {
|
2020-09-09 18:18:55 +02:00
|
|
|
bool interesting_message = false;
|
|
|
|
if (obus_can::receive(message)) {
|
2020-10-22 21:16:12 +02:00
|
|
|
if (is_from_controller(message->from)) {
|
2020-09-09 18:18:55 +02:00
|
|
|
switch (message->msg_type) {
|
|
|
|
case OBUS_MSGTYPE_C_INFOSTART:
|
2020-09-09 22:00:59 +02:00
|
|
|
{
|
2021-02-03 01:25:03 +01:00
|
|
|
randomSeed(message->infostart.seed);
|
2020-09-09 22:00:59 +02:00
|
|
|
uint8_t info_message[OBUS_PAYLD_INFO_MAXLEN];
|
|
|
|
int len = info_generator(info_message);
|
|
|
|
obus_can::send_i_infomessage(this_module, info_message, len);
|
|
|
|
}
|
2020-09-09 18:18:55 +02:00
|
|
|
break;
|
|
|
|
case OBUS_MSGTYPE_C_STATE:
|
|
|
|
interesting_message = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return interesting_message;
|
2020-08-26 21:13:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void strike() {
|
2020-08-27 05:30:22 +02:00
|
|
|
if (!active) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-26 21:13:16 +02:00
|
|
|
strike_count++;
|
2020-09-09 18:30:44 +02:00
|
|
|
_setLedBlink(COLOR_RED, BLINK_DELAY_FAST);
|
|
|
|
led_reset_time = millis() + 2000;
|
2020-08-26 21:13:16 +02:00
|
|
|
obus_can::send_m_strike(this_module, strike_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
void solve() {
|
2020-08-27 05:30:22 +02:00
|
|
|
if (!active) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-26 21:13:16 +02:00
|
|
|
obus_can::send_m_solved(this_module);
|
2020-08-27 05:30:22 +02:00
|
|
|
active = false;
|
2020-09-09 18:30:44 +02:00
|
|
|
_setLed(COLOR_GREEN);
|
2020-08-27 05:30:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_active() {
|
|
|
|
return active;
|
2020-08-26 21:13:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|