/* main.c * * * this is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * this is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include #include "board.h" #include "net/ipv6/addr.h" #include "net/gnrc.h" #include "net/gnrc/netif.h" #include "shell.h" #include "periph/flashpage.h" #include "thread.h" #include "periph/uart.h" #include "periph/gpio.h" #include "xtimer.h" #include "ringbuffer.h" #include "roomba.h" #include "song.h" #include "mutex.h" #include "msg.h" #ifndef SHELL_BUFSIZE #define SHELL_BUFSIZE (128U) #endif #define DISPATCHER_PRIO (THREAD_PRIORITY_MAIN - 1) #define BT_PRIO (THREAD_PRIORITY_MAIN - 1) #define MSGQ_SIZE (4) #define GPIO_WAKEUP GPIO_PIN(1,11) #define SENSOR_BUFFER_SIZE (sizeof(struct sensors_code1) + sizeof(struct sensors_code2) + \ sizeof(struct sensors_code3)) #define MSG_SENSORS_OFF (0x4000) #define MSG_SENSORS_ON (0x4100) #define MSG_SENSORS_AVAILABLE (0x4200) #define MSG_SENSORS_TIMEOUT (0x4300) const char roomba_modstring[6][6] = { "OFF", "DRIVE", "FULL", "CLEAN", "SPOT", "DOCK" }; extern const uint16_t song0[], song1[], song2[], song3[], song4[], song5[]; // Types typedef struct { char rx_mem[SENSOR_BUFFER_SIZE]; ringbuffer_t rx_buf; } uart_ctx_t; // Globals // static mutex_t roomba_mutex = MUTEX_INIT; static uart_ctx_t ctx[UART_NUMOF]; static kernel_pid_t order66_pid, sensors_pid, gatt_srv_pid; static char order66_stack[THREAD_STACKSIZE_MAIN]; static char sensors_stack[THREAD_STACKSIZE_MAIN]; static char gatt_srv_stack[THREAD_STACKSIZE_MAIN]; static msg_t sensors_msgq[MSGQ_SIZE]; static msg_t gatt_srv_msgq[MSGQ_SIZE]; static xtimer_t sensors_msg_timer; const uint16_t *songs[] = { song0, song1, song2, song3, song4, song5 }; const uint32_t part_len[] = { 5000000, 5000000, 3200000, 2500000, 4200000, 4200000 }; // Prototypes void *order66(void *arg); void *sensors_loop(void *arg); static void roomba_cmd(uint8_t cmd, uint8_t *payload, uint8_t len); void roomba_led(uint8_t status, uint8_t spot, uint8_t clean, uint8_t max, uint8_t power_c, uint8_t power_i); int song_len(const uint16_t *s) { int l = (s[0] & 0xFF00) >> 8; return l; } uint32_t load_song(int n) { uint8_t cmd = ROOMBA_SONG; int l = song_len(songs[n]); int i; mutex_lock(&roomba_mutex); uart_t dev = UART_DEV(0); uart_write(dev, &cmd, 1); uart_write(dev, (const uint8_t *)songs[n], 2 + (l << 1)); mutex_unlock(&roomba_mutex); xtimer_usleep(100000); return part_len[n]; } // Order 66 void *order66(void *arg) { uint8_t x = 0; uint32_t st; st = load_song(0); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(1); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(2); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(3); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(4); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(2); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(3); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); st = load_song(5); xtimer_usleep(20000); roomba_cmd(ROOMBA_PLAY, &x, 1); xtimer_usleep(st); return NULL; } static int roomba_mode = ROOMBA_MODE_OFF; static void roomba_stop(void) { uint8_t cmd = ROOMBA_POWER; uart_t dev = UART_DEV(0); mutex_lock(&roomba_mutex); uart_write(dev, &cmd, 1); mutex_unlock(&roomba_mutex); xtimer_usleep(200000); } static void roomba_start(void) { gpio_clear(GPIO_WAKEUP); xtimer_usleep(200000); gpio_set(GPIO_WAKEUP); uint8_t cmd = ROOMBA_START; uart_t dev = UART_DEV(0); mutex_lock(&roomba_mutex); uart_write(dev, &cmd, 1); xtimer_usleep(20000); mutex_unlock(&roomba_mutex); } static void roomba_safe_to_full(void) { uint8_t cmd = ROOMBA_FULL; uart_t dev = UART_DEV(0); mutex_lock(&roomba_mutex); uart_write(dev, &cmd, 1); xtimer_usleep(20000); mutex_unlock(&roomba_mutex); } static void roomba_full_to_safe(void) { uint8_t cmd = ROOMBA_SAFE; uart_t dev = UART_DEV(0); mutex_lock(&roomba_mutex); uart_write(dev, &cmd, 1); xtimer_usleep(20000); mutex_unlock(&roomba_mutex); } static void roomba_cmd(uint8_t cmd, uint8_t *payload, uint8_t len) { uart_t dev = UART_DEV(0); mutex_lock(&roomba_mutex); uart_write(dev, &cmd, 1); if(len && payload) uart_write(dev, payload, len); mutex_unlock(&roomba_mutex); } int switch_mode(int newmode) { msg_t msg; int oldmode = roomba_mode; if (roomba_mode != newmode) { if (roomba_mode == ROOMBA_MODE_OFF) { roomba_start(); roomba_mode = ROOMBA_MODE_DRIVE; roomba_led(0x03, 0, 0, 0, 0x80, 0x80); } switch(newmode) { case ROOMBA_MODE_OFF: roomba_stop(); roomba_led(0x00, 0, 0, 0, 0xF0, 0x20); break; case ROOMBA_MODE_DRIVE: if (roomba_mode == newmode) break; else if (roomba_mode == ROOMBA_MODE_FULL) roomba_full_to_safe(); else { roomba_stop(); roomba_start(); } roomba_led(0x03, 0, 0, 0, 0x80, 0x80); break; case ROOMBA_MODE_FULL: if (roomba_mode == ROOMBA_MODE_DRIVE) roomba_safe_to_full(); else { roomba_stop(); roomba_start(); roomba_safe_to_full(); } roomba_led(0x01, 0, 0, 0, 0xF0, 0xFF); break; case ROOMBA_MODE_CLEAN: roomba_cmd(ROOMBA_CLEAN, NULL, 0); break; case ROOMBA_MODE_SPOT: roomba_cmd(ROOMBA_SPOT, NULL, 0); break; case ROOMBA_MODE_DOCK: roomba_cmd(ROOMBA_DOCK, NULL, 0); break; case 66: order66_pid = thread_create(order66_stack, sizeof(order66_stack), DISPATCHER_PRIO, 0, order66, NULL, "ORDER66"); newmode = roomba_mode; break; } printf("Mode: %s\n", roomba_modstring[newmode]); roomba_mode = newmode; if (roomba_mode == ROOMBA_MODE_FULL) { msg.type = MSG_SENSORS_ON; msg_send(&msg, sensors_pid); } if (oldmode == ROOMBA_MODE_FULL) { msg.type = MSG_SENSORS_OFF; msg_send(&msg, sensors_pid); } } return 0; } uint8_t get_mode(void) { return roomba_mode; } static int cmd_info(int argc, char **argv) { (void)argc; (void)argv; puts("iRobot ROOMBA 500 powered by RIOT-OS"); printf("You are running RIOT on %s.\n", RIOT_BOARD); printf("This board features a %s MCU.\n", RIOT_MCU); puts("\nUART INFO:"); printf("Available devices: %i\n", UART_NUMOF); if (STDIO_UART_DEV != UART_UNDEF) { printf("UART used for STDIO (the shell): UART_DEV(%i)\n\n", STDIO_UART_DEV); } return 0; } int cmd_mode(int argc, char **argv) { int i; if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } for (i = 0; i < 6; i++) { if (strcmp(argv[1], roomba_modstring[i]) == 0) { switch_mode(i); return 0; } } if (strcmp(argv[1], "ORDER66") == 0) { order66_pid = thread_create(order66_stack, sizeof(order66_stack), DISPATCHER_PRIO, 0, order66, NULL, "ORDER66"); return 0; } printf("Mode '%s' unknown\r\n", argv[1]); return 1; } static int cmd_play(int argc, char **argv) { uint8_t x = 0, z = 0; if ((argc != 2) || (strlen(argv[1]) != 1)) { printf("usage: %s \n", argv[0]); return 1; } x = argv[1][0] - '0'; load_song(x); roomba_cmd(ROOMBA_PLAY, &z, 1); return 0; } #define ALLSENSORS_CODE (0) #define ALLSENSORS_SIZE (26) #define QUICKSENSORS_CODE (1) #define QUICKSENSORS_SIZE (10) static volatile uint8_t sensors_expect_rx = 0; static volatile uint8_t sensors_data_rx = 0; static uint8_t raw_sensors_data[ALLSENSORS_SIZE]; static void invalidate_sensors(void) { msg_t msg; memset(&raw_sensors_data, 0, sizeof(struct roomba_sensors_data)); msg.type = MSG_SENSORS_AVAILABLE; msg_send(&msg, gatt_srv_pid); } static int read_sensors(uint8_t mode) { uint8_t cmd[2] = {ROOMBA_SENSORS, 0}; int len = -1; msg_t msg; uart_t dev = UART_DEV(0); msg_t msg_timeout; msg_timeout.type = MSG_SENSORS_TIMEOUT; if (mode == ALLSENSORS_CODE) sensors_expect_rx = ALLSENSORS_SIZE; else sensors_expect_rx = QUICKSENSORS_SIZE; cmd[1] = mode; mutex_lock(&roomba_mutex); sensors_data_rx = 0; uart_write(dev, cmd, 1); uart_write(dev, cmd + 1, 1); xtimer_set_msg(&sensors_msg_timer, 500000, &msg_timeout, sensors_pid); if (msg_receive(&msg) >= 0) { if (msg.type == MSG_SENSORS_AVAILABLE) { len = sensors_expect_rx; } else if (msg.type == MSG_SENSORS_TIMEOUT) { printf("Timeout while reading sensors\n"); invalidate_sensors(); } else if (msg.type == MSG_SENSORS_OFF) { printf("Paused sensor task.\n"); invalidate_sensors(); } else { printf("Invalid message type %04x\n", msg.type); invalidate_sensors(); } sensors_expect_rx = 0; } mutex_unlock(&roomba_mutex); return len; } // Rx callback static void rx_cb(void *arg, uint8_t data) { uart_t dev = (uart_t)arg; msg_t msg; //printf("RX: %02x\n", data); if (sensors_expect_rx > sensors_data_rx) raw_sensors_data[sensors_data_rx++] = data; if (sensors_expect_rx == sensors_data_rx) { msg.type = MSG_SENSORS_AVAILABLE; msg_send(&msg, sensors_pid); } } static struct roomba_sensors_data roomba_sensors_data = { }; void print_sensors(void) { struct roomba_sensors_data *r = &roomba_sensors_data; printf("=== Sensors ===\n"); printf("Valid : %s\n", r->valid?"yes":"no"); if (r->valid) { printf("Drops : C:%d L:%d R:%d\n", r->drop_caster, r->drop_left, r->drop_right); printf("Bumps : L: %d R: %d\n", r->bump_left, r->bump_right); printf("Cliffs: FL:%d FR:%d \n", r->cliff_frontleft, r->cliff_frontright); printf(" L:%d R:%d \n", r->cliff_left, r->cliff_right); printf("OVC: %02x\n", r->ovc); } printf("==============\n\n"); } void *sensors_loop(void *arg) { int ret = 0; msg_t msg; struct roomba_sensors_data tmp; while (1) { if (get_mode() != ROOMBA_MODE_FULL) { while (1) { if ((msg_receive(&msg) >= 0) && (msg.type == MSG_SENSORS_ON)) { printf("Sensors thread: resume.\n"); break; } } } xtimer_usleep(250000); ret = read_sensors(ALLSENSORS_CODE); if (ret < 0) { xtimer_usleep(1000000); } else { /* Byte 0: Wheeldrops + Bumps */ tmp.drop_caster = !!(raw_sensors_data[0] & (1 << SENSE_DROP_CASTER)); tmp.drop_left = !!(raw_sensors_data[0] & (1 << SENSE_DROP_LEFT)); tmp.drop_right = !!(raw_sensors_data[0] & (1 << SENSE_DROP_RIGHT)); tmp.bump_left = !!(raw_sensors_data[0] & (1 << SENSE_BUMP_LEFT)); tmp.bump_right = !!(raw_sensors_data[0] & (1 << SENSE_BUMP_RIGHT)); /* Byte 1: Wall */ tmp.wall = raw_sensors_data[1]; /* Byte 2: Cliff L */ tmp.cliff_left = raw_sensors_data[2]; /* Byte 3: Cliff FL */ tmp.cliff_frontleft = raw_sensors_data[3]; /* Byte 4: Cliff FR */ tmp.cliff_frontright = raw_sensors_data[4]; /* Byte 5: Cliff R */ tmp.cliff_right = raw_sensors_data[5]; /* Byte 7: Overcurrents */ tmp.ovc = raw_sensors_data[7]; /* 'valid' flag */ tmp.valid = 1; } if (memcmp(&roomba_sensors_data, &tmp, sizeof(struct roomba_sensors_data)) != 0) { msg_t msg; memcpy(&roomba_sensors_data, &tmp, sizeof(struct roomba_sensors_data)); print_sensors(); msg.type = MSG_SENSORS_AVAILABLE; msg_send(&msg, gatt_srv_pid); } } } struct roomba_sensors_data *roomba_sensors(void) { return &roomba_sensors_data; } void roomba_led(uint8_t status, uint8_t spot, uint8_t clean, uint8_t max, uint8_t power_c, uint8_t power_i) { uint8_t led_bytes[3]; led_bytes[0] = ((status & 0x03) << 4) | ((spot & 0x01) << 3) | ((clean & 0x01) << 2) | ((max &0x01) << 1); led_bytes[1] = power_c; led_bytes[2] = power_i; roomba_cmd(ROOMBA_LEDS, led_bytes, 3); } void *gatt_srv(void*); static const shell_command_t shell_commands[] = { { "mode", "Switch mode", cmd_mode }, { "play", "play song", cmd_play }, { "info", "device info", cmd_info }, { NULL, NULL, NULL } }; int main(void) { /* initialize UART */ char line_buf[SHELL_BUFSIZE]; uart_init(UART_DEV(0), 115200, rx_cb, (void *)0); /* initialize ringbuffers */ for (unsigned i = 0; i < UART_NUMOF; i++) { ringbuffer_init(&(ctx[i].rx_buf), ctx[i].rx_mem, SENSOR_BUFFER_SIZE); } /* initialize GPIO WAKEUP */ gpio_init(GPIO_WAKEUP, GPIO_OUT); gpio_set(GPIO_WAKEUP); /* Gatt Server */ gatt_srv_pid = thread_create(gatt_srv_stack, sizeof(gatt_srv_stack), DISPATCHER_PRIO - 1, 0, gatt_srv, NULL, "BLE_gatt"); /* Create Sensors thread */ msg_init_queue(sensors_msgq, MSGQ_SIZE); sensors_pid = thread_create(sensors_stack, sizeof(sensors_stack), DISPATCHER_PRIO, 0, sensors_loop, NULL, "sensors"); /* run the shell */ shell_run(shell_commands, line_buf, SHELL_BUFSIZE); return 0; }