/* r2d2 - interact with Sphero R2-D2 Copyright (C) 2020 Ian Cowburn This program 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 3 of the License, or (at your option) any later version. This program 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, see . */ #include #include #include #include #include #include #include #include #include #include typedef struct { const char *name; int command[20]; } Command; static Command plain_commands[] = { {"laugh", {0x0A,0x18,0x00,0x1F,0x00,0x32,0x00,0x00,0x00,0x00,0x00,-1}}, {"yes", {0x0A,0x17,0x05,0x41,0x00,0x0F,-1}}, {"no", {0x0A,0x17,0x05,0x3F,0x00,0x10,-1}}, {"alarm", {0x0A,0x17,0x05,0x17,0x00,0x07,-1}}, {"angry", {0x0A,0x17,0x05,0x18,0x00,0x08,-1}}, {"annoyed", {0x0A,0x17,0x05,0x19,0x00,0x09,-1}}, {"ionblast", {0x0A,0x17,0x05,0x1A,0x00,0x0E,-1}}, {"sad", {0x0A,0x17,0x05,0x1C,0x00,0x11,-1}}, {"scared", {0x0A,0x17,0x05,0x1D,0x00,0x13,-1}}, {"chatty", {0x0A,0x17,0x05,0x17,0x00,0x0A,-1}}, {"confident", {0x0A,0x17,0x05,0x18,0x00,0x12,-1}}, {"excited", {0x0A,0x17,0x05,0x19,0x00,0x0C,-1}}, {"happy", {0x0A,0x17,0x05,0x1A,0x00,0x0D,-1}}, {"laugh", {0x0A,0x17,0x05,0x1B,0x00,0x0F,-1}}, {"surprise", {0x0A,0x17,0x05,0x1C,0x00,0x18,-1}}, {"tripod", {0x0A,0x17,0x0D,0x1D,0x01,-1}}, {"bipod", {0x0A,0x17,0x0D,0x1C,0x02,-1}}, {NULL, {0}} }; static unsigned char force_command[] = { 0x75,0x73,0x65,0x74,0x68,0x65,0x66,0x6F,0x72,0x63,0x65,0x2E, 0x2E,0x2E,0x62,0x61,0x6E,0x64 }; static Command wake_command = { "wakeup", { 0x0A,0x13,0x0D,0x00,-1 } }; static void Fatal(const char *call) { perror(call); exit(EXIT_FAILURE); } static unsigned char Checksum(const char *p, int len) { unsigned char c; int f; c = 0; for(f = 0; f < len; f++) { c += p[f]; } c = ~c; return c; } static int Write(int sock, unsigned char *buff, int len) { int wr = 0; int tot = 0; while(tot < len) { wr = write(sock, buff + tot, len - tot); if (wr < 1) { return -1; } tot += wr; } return len; } static void CharWriteHandle(int sock, int handle, const unsigned char *cmd, int len) { unsigned char buff[1024]; unsigned short *us; memcpy(buff + 3, cmd, len); len += 3; buff[0] = 0x12; us = (unsigned short *)(buff + 1); *us = htobs(handle); if (Write(sock, buff, len) == -1) { Fatal("write"); } } static void SendCommand(int sock, int handle, const Command *cmd) { unsigned char buff[1024]; unsigned short *us; int len; int f; for(f = 0; cmd->command[f] != -1; f++) { buff[f] = (unsigned char)cmd->command[f]; } len = f; buff[len] = Checksum(buff, len); len++; memmove(buff + 1, buff, len++); buff[0] = 0x8du; buff[++len] = 0xd8u; CharWriteHandle(sock, handle, buff, len); } int main(int argc, char *argv[]) { char address[32] = {0}; struct sockaddr_l2 src_addr = {0}; struct sockaddr_l2 dest_addr = {0}; int base; int f; int sock; /* Get address */ if (getenv("R2D2")) { snprintf(address, sizeof address, "%s", getenv("R2D2")); } if (argc > 2 && strcmp(argv[1], "-a") == 0) { snprintf(address, sizeof address, "%s", argv[2]); base = 3; } else { base = 1; } /* Check address and arguments supplied */ if (!address[0]) { fprintf(stderr, "%s: pass address or set R2D2 variable\n", argv[0]); return EXIT_FAILURE; } if (argc <= base) { fprintf(stderr, "%s: usage %s [-a address] command [..command]\n", argv[0], argv[0]); return EXIT_FAILURE; } /* Open connection to R2 */ sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock == -1) { Fatal("socket"); } src_addr.l2_family = AF_BLUETOOTH; src_addr.l2_cid = htobs(4); bacpy(&src_addr.l2_bdaddr, BDADDR_ANY); src_addr.l2_bdaddr_type = BDADDR_LE_RANDOM; if (bind(sock, (void *)&src_addr, sizeof(src_addr)) == -1) { Fatal("bind"); } dest_addr.l2_family = AF_BLUETOOTH; dest_addr.l2_cid = htobs(4); str2ba(address, &dest_addr.l2_bdaddr); dest_addr.l2_bdaddr_type = BDADDR_LE_RANDOM; if (connect(sock, (void *)&dest_addr, sizeof(dest_addr)) == -1) { Fatal("connect"); } CharWriteHandle(sock, 0x15, force_command, sizeof force_command); SendCommand(sock, 0x1c, &wake_command); for(f = base; f < argc; f++) { int n; Command *cmd = NULL; for(n = 0; plain_commands[n].name && !cmd; n++) { if (strcmp(argv[f], plain_commands[n].name) == 0) { cmd = plain_commands + n; } } if (cmd == NULL) { fprintf(stderr, "%s: unknown command %s\n", argv[0], argv[f]); } else { SendCommand(sock, 0x1c, cmd); } } /* All done */ if (close(sock) == -1) { Fatal("close"); } return EXIT_SUCCESS; }