├── .travis.yml ├── LICENSE ├── README.md ├── SoftwareSerial.cpp ├── SoftwareSerial.h ├── example └── RoombaLibTesting │ └── RoombaLibTesting.ino ├── iRobotCreate2.cpp └── iRobotCreate2.h /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | before_install: 3 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" 4 | - sleep 3 5 | - export DISPLAY=:1.0 6 | - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz 7 | - tar xf arduino-1.6.5-linux64.tar.xz 8 | - sudo mv arduino-1.6.5 /usr/local/share/arduino 9 | - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino 10 | install: 11 | - ln -s $PWD /usr/local/share/arduino/libraries/iRobotCreate2 12 | #i gues there is no harm in linking all the files 13 | # - cp -f $PWD/*.h /usr/local/share/arduino/libraries/iRobotCreate2 14 | # - cp -f $PWD/*.cpp /usr/local/share/arduino/libraries/iRobotCreate2 15 | script: 16 | - arduino --verify --board arduino:avr:uno $PWD/example/RoombaLibTesting/RoombaLibTesting.ino 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dominic Amato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iRobot-Create2-Arduino [![Build Status](https://travis-ci.org/brinnLabs/Create2.svg?branch=master)](https://travis-ci.org/brinnLabs/Create2) 2 | ====================== 3 | 4 | An Arduino library for the iRobot Open Interface Spec 2 for newer Roombas and the Create 2 5 | 6 | ##To-Do 7 | + Easy Command abstraction e.g. move(FORWARD, 10, FEET); 8 | + Blocking and Non-Blocking Movement Command 9 | + Separate Files for object and commands 10 | -------------------------------------------------------------------------------- /SoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - 3 | Multi-instance software serial library for Arduino/Wiring 4 | -- Interrupt-driven receive and other improvements by ladyada 5 | (http://ladyada.net) 6 | -- Tuning, circular buffer, derivation from class Print/Stream, 7 | multi-instance support, porting to 8MHz processors, 8 | various optimizations, PROGMEM delay tables, inverse logic and 9 | direct port writing by Mikal Hart (http://www.arduiniana.org) 10 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 11 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 12 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 13 | 14 | This library is free software; you can redistribute it and/or 15 | modify it under the terms of the GNU Lesser General Public 16 | License as published by the Free Software Foundation; either 17 | version 2.1 of the License, or (at your option) any later version. 18 | 19 | This library is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | 24 | You should have received a copy of the GNU Lesser General Public 25 | License along with this library; if not, write to the Free Software 26 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 | 28 | The latest version of this library can always be found at 29 | http://arduiniana.org. 30 | */ 31 | 32 | // When set, _DEBUG co-opts pins 11 and 13 for debugging with an 33 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 34 | // the bit times, so don't rely on it too much at high baud rates 35 | #define _DEBUG 0 36 | #define _DEBUG_PIN1 11 37 | #define _DEBUG_PIN2 13 38 | // 39 | // Includes 40 | // 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | 47 | #if defined(__MK20DX128__) || defined(__MK20DX256__) 48 | 49 | SoftwareSerial::SoftwareSerial(){ 50 | 51 | } 52 | SoftwareSerial::SoftwareSerial(uint8_t rxPin, uint8_t txPin, bool inverse_logic /* = false */) 53 | { 54 | buffer_overflow = false; 55 | if (rxPin == 0 && txPin == 1) { 56 | port = &Serial1; 57 | return; 58 | } else if (rxPin == 9 && txPin == 10) { 59 | port = &Serial2; 60 | return; 61 | } else if (rxPin == 7 && txPin == 8) { 62 | port = &Serial3; 63 | return; 64 | } 65 | port = NULL; 66 | pinMode(txPin, OUTPUT); 67 | pinMode(rxPin, INPUT_PULLUP); 68 | txpin = txPin; 69 | rxpin = rxPin; 70 | txreg = portOutputRegister(digitalPinToPort(txPin)); 71 | rxreg = portInputRegister(digitalPinToPort(rxPin)); 72 | cycles_per_bit = 0; 73 | } 74 | 75 | void SoftwareSerial::begin(unsigned long speed) 76 | { 77 | if (port) { 78 | port->begin(speed); 79 | } else { 80 | cycles_per_bit = (uint32_t)(F_CPU + speed / 2) / speed; 81 | ARM_DEMCR |= ARM_DEMCR_TRCENA; 82 | ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; 83 | } 84 | } 85 | 86 | void SoftwareSerial::end() 87 | { 88 | if (port) { 89 | port->end(); 90 | port = NULL; 91 | } else { 92 | pinMode(txpin, INPUT); 93 | pinMode(rxpin, INPUT); 94 | } 95 | cycles_per_bit = 0; 96 | } 97 | 98 | // The worst case expected length of any interrupt routines. If an 99 | // interrupt runs longer than this number of cycles, it can disrupt 100 | // the transmit waveform. Increasing this number causes SoftwareSerial 101 | // to hog the CPU longer, delaying all interrupt response for other 102 | // libraries, so this should be made as small as possible but still 103 | // ensure accurate transmit waveforms. 104 | #define WORST_INTERRUPT_CYCLES 360 105 | 106 | static void wait_for_target(uint32_t begin, uint32_t target) 107 | { 108 | if (target - (ARM_DWT_CYCCNT - begin) > WORST_INTERRUPT_CYCLES+20) { 109 | uint32_t pretarget = target - WORST_INTERRUPT_CYCLES; 110 | //digitalWriteFast(12, HIGH); 111 | interrupts(); 112 | while (ARM_DWT_CYCCNT - begin < pretarget) ; // wait 113 | noInterrupts(); 114 | //digitalWriteFast(12, LOW); 115 | } 116 | while (ARM_DWT_CYCCNT - begin < target) ; // wait 117 | } 118 | 119 | size_t SoftwareSerial::write(uint8_t b) 120 | { 121 | elapsedMicros elapsed; 122 | uint32_t target; 123 | uint8_t mask; 124 | uint32_t begin_cycle; 125 | 126 | // use hardware serial, if possible 127 | if (port) return port->write(b); 128 | if (cycles_per_bit == 0) return 0; 129 | ARM_DEMCR |= ARM_DEMCR_TRCENA; 130 | ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; 131 | // start bit 132 | target = cycles_per_bit; 133 | noInterrupts(); 134 | begin_cycle = ARM_DWT_CYCCNT; 135 | *txreg = 0; 136 | wait_for_target(begin_cycle, target); 137 | // 8 data bits 138 | for (mask = 1; mask; mask <<= 1) { 139 | *txreg = (b & mask) ? 1 : 0; 140 | target += cycles_per_bit; 141 | wait_for_target(begin_cycle, target); 142 | } 143 | // stop bit 144 | *txreg = 1; 145 | interrupts(); 146 | target += cycles_per_bit; 147 | while (ARM_DWT_CYCCNT - begin_cycle < target) ; // wait 148 | return 1; 149 | } 150 | 151 | void SoftwareSerial::flush() 152 | { 153 | if (port) port->flush(); 154 | } 155 | 156 | // TODO implement reception using pin change DMA capturing 157 | // ARM_DWT_CYCCNT and the bitband mapped GPIO_PDIR register 158 | // to a circular buffer (8 bytes per event... memory intensive) 159 | 160 | int SoftwareSerial::available() 161 | { 162 | if (port) return port->available(); 163 | return 0; 164 | } 165 | 166 | int SoftwareSerial::peek() 167 | { 168 | if (port) return port->peek(); 169 | return -1; 170 | } 171 | 172 | int SoftwareSerial::read() 173 | { 174 | if (port) return port->read(); 175 | return -1; 176 | } 177 | 178 | #else 179 | 180 | // 181 | // Lookup table 182 | // 183 | typedef struct _DELAY_TABLE 184 | { 185 | long baud; 186 | unsigned short rx_delay_centering; 187 | unsigned short rx_delay_intrabit; 188 | unsigned short rx_delay_stopbit; 189 | unsigned short tx_delay; 190 | } DELAY_TABLE; 191 | 192 | #if F_CPU == 16000000 193 | 194 | static const DELAY_TABLE PROGMEM table[] = 195 | { 196 | // baud rxcenter rxintra rxstop tx 197 | { 115200, 1, 17, 17, 12, }, 198 | { 57600, 10, 37, 37, 33, }, 199 | { 38400, 25, 57, 57, 54, }, 200 | { 31250, 31, 70, 70, 68, }, 201 | { 28800, 34, 77, 77, 74, }, 202 | { 19200, 54, 117, 117, 114, }, 203 | { 14400, 74, 156, 156, 153, }, 204 | { 9600, 114, 236, 236, 233, }, 205 | { 4800, 233, 474, 474, 471, }, 206 | { 2400, 471, 950, 950, 947, }, 207 | { 1200, 947, 1902, 1902, 1899, }, 208 | { 600, 1902, 3804, 3804, 3800, }, 209 | { 300, 3804, 7617, 7617, 7614, }, 210 | }; 211 | 212 | const int XMIT_START_ADJUSTMENT = 5; 213 | 214 | #elif F_CPU == 8000000 215 | 216 | static const DELAY_TABLE table[] PROGMEM = 217 | { 218 | // baud rxcenter rxintra rxstop tx 219 | { 115200, 1, 5, 5, 3, }, 220 | { 57600, 1, 15, 15, 13, }, 221 | { 38400, 2, 25, 26, 23, }, 222 | { 31250, 7, 32, 33, 29, }, 223 | { 28800, 11, 35, 35, 32, }, 224 | { 19200, 20, 55, 55, 52, }, 225 | { 14400, 30, 75, 75, 72, }, 226 | { 9600, 50, 114, 114, 112, }, 227 | { 4800, 110, 233, 233, 230, }, 228 | { 2400, 229, 472, 472, 469, }, 229 | { 1200, 467, 948, 948, 945, }, 230 | { 600, 948, 1895, 1895, 1890, }, 231 | { 300, 1895, 3805, 3805, 3802, }, 232 | }; 233 | 234 | const int XMIT_START_ADJUSTMENT = 4; 235 | 236 | #elif F_CPU == 20000000 237 | 238 | // 20MHz support courtesy of the good people at macegr.com. 239 | // Thanks, Garrett! 240 | 241 | static const DELAY_TABLE PROGMEM table[] = 242 | { 243 | // baud rxcenter rxintra rxstop tx 244 | { 115200, 3, 21, 21, 18, }, 245 | { 57600, 20, 43, 43, 41, }, 246 | { 38400, 37, 73, 73, 70, }, 247 | { 31250, 45, 89, 89, 88, }, 248 | { 28800, 46, 98, 98, 95, }, 249 | { 19200, 71, 148, 148, 145, }, 250 | { 14400, 96, 197, 197, 194, }, 251 | { 9600, 146, 297, 297, 294, }, 252 | { 4800, 296, 595, 595, 592, }, 253 | { 2400, 592, 1189, 1189, 1186, }, 254 | { 1200, 1187, 2379, 2379, 2376, }, 255 | { 600, 2379, 4759, 4759, 4755, }, 256 | { 300, 4759, 9523, 9523, 9520, }, 257 | }; 258 | 259 | const int XMIT_START_ADJUSTMENT = 6; 260 | 261 | #else 262 | 263 | #error This version of SoftwareSerial supports only 20, 16 and 8MHz processors 264 | 265 | #endif 266 | 267 | // 268 | // Statics 269 | // 270 | SoftwareSerial *SoftwareSerial::active_object = 0; 271 | char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; 272 | volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; 273 | volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; 274 | 275 | // 276 | // Debugging 277 | // 278 | // This function generates a brief pulse 279 | // for debugging or measuring on an oscilloscope. 280 | inline void DebugPulse(uint8_t pin, uint8_t count) 281 | { 282 | #if _DEBUG 283 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 284 | 285 | uint8_t val = *pport; 286 | while (count--) 287 | { 288 | *pport = val | digitalPinToBitMask(pin); 289 | *pport = val; 290 | } 291 | #endif 292 | } 293 | 294 | // 295 | // Private methods 296 | // 297 | 298 | /* static */ 299 | inline void SoftwareSerial::tunedDelay(uint16_t delay) { 300 | uint8_t tmp=0; 301 | 302 | asm volatile("sbiw %0, 0x01 \n\t" 303 | "ldi %1, 0xFF \n\t" 304 | "cpi %A0, 0xFF \n\t" 305 | "cpc %B0, %1 \n\t" 306 | "brne .-10 \n\t" 307 | : "+r" (delay), "+a" (tmp) 308 | : "0" (delay) 309 | ); 310 | } 311 | 312 | // This function sets the current object as the "listening" 313 | // one and returns true if it replaces another 314 | bool SoftwareSerial::listen() 315 | { 316 | if (active_object != this) 317 | { 318 | _buffer_overflow = false; 319 | uint8_t oldSREG = SREG; 320 | cli(); 321 | _receive_buffer_head = _receive_buffer_tail = 0; 322 | active_object = this; 323 | SREG = oldSREG; 324 | return true; 325 | } 326 | 327 | return false; 328 | } 329 | 330 | // 331 | // The receive routine called by the interrupt handler 332 | // 333 | void SoftwareSerial::recv() 334 | { 335 | 336 | #if GCC_VERSION < 40302 337 | // Work-around for avr-gcc 4.3.0 OSX version bug 338 | // Preserve the registers that the compiler misses 339 | // (courtesy of Arduino forum user *etracer*) 340 | asm volatile( 341 | "push r18 \n\t" 342 | "push r19 \n\t" 343 | "push r20 \n\t" 344 | "push r21 \n\t" 345 | "push r22 \n\t" 346 | "push r23 \n\t" 347 | "push r26 \n\t" 348 | "push r27 \n\t" 349 | ::); 350 | #endif 351 | 352 | uint8_t d = 0; 353 | 354 | // If RX line is high, then we don't see any start bit 355 | // so interrupt is probably not for us 356 | if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) 357 | { 358 | // Wait approximately 1/2 of a bit width to "center" the sample 359 | tunedDelay(_rx_delay_centering); 360 | DebugPulse(_DEBUG_PIN2, 1); 361 | 362 | // Read each of the 8 bits 363 | for (uint8_t i=0x1; i; i <<= 1) 364 | { 365 | tunedDelay(_rx_delay_intrabit); 366 | DebugPulse(_DEBUG_PIN2, 1); 367 | uint8_t noti = ~i; 368 | if (rx_pin_read()) 369 | d |= i; 370 | else // else clause added to ensure function timing is ~balanced 371 | d &= noti; 372 | } 373 | 374 | // skip the stop bit 375 | tunedDelay(_rx_delay_stopbit); 376 | DebugPulse(_DEBUG_PIN2, 1); 377 | 378 | if (_inverse_logic) 379 | d = ~d; 380 | 381 | // if buffer full, set the overflow flag and return 382 | if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) 383 | { 384 | // save new data in buffer: tail points to where byte goes 385 | _receive_buffer[_receive_buffer_tail] = d; // save new byte 386 | _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; 387 | } 388 | else 389 | { 390 | #if _DEBUG // for scope: pulse pin as overflow indictator 391 | DebugPulse(_DEBUG_PIN1, 1); 392 | #endif 393 | _buffer_overflow = true; 394 | } 395 | } 396 | 397 | #if GCC_VERSION < 40302 398 | // Work-around for avr-gcc 4.3.0 OSX version bug 399 | // Restore the registers that the compiler misses 400 | asm volatile( 401 | "pop r27 \n\t" 402 | "pop r26 \n\t" 403 | "pop r23 \n\t" 404 | "pop r22 \n\t" 405 | "pop r21 \n\t" 406 | "pop r20 \n\t" 407 | "pop r19 \n\t" 408 | "pop r18 \n\t" 409 | ::); 410 | #endif 411 | } 412 | 413 | void SoftwareSerial::tx_pin_write(uint8_t pin_state) 414 | { 415 | if (pin_state == LOW) 416 | *_transmitPortRegister &= ~_transmitBitMask; 417 | else 418 | *_transmitPortRegister |= _transmitBitMask; 419 | } 420 | 421 | uint8_t SoftwareSerial::rx_pin_read() 422 | { 423 | return *_receivePortRegister & _receiveBitMask; 424 | } 425 | 426 | // 427 | // Interrupt handling 428 | // 429 | 430 | /* static */ 431 | inline void SoftwareSerial::handle_interrupt() 432 | { 433 | if (active_object) 434 | { 435 | active_object->recv(); 436 | } 437 | } 438 | 439 | #if defined(PCINT0_vect) 440 | ISR(PCINT0_vect) 441 | { 442 | SoftwareSerial::handle_interrupt(); 443 | } 444 | #endif 445 | 446 | #if defined(PCINT1_vect) 447 | ISR(PCINT1_vect) 448 | { 449 | SoftwareSerial::handle_interrupt(); 450 | } 451 | #endif 452 | 453 | #if defined(PCINT2_vect) 454 | ISR(PCINT2_vect) 455 | { 456 | SoftwareSerial::handle_interrupt(); 457 | } 458 | #endif 459 | 460 | #if defined(PCINT3_vect) 461 | ISR(PCINT3_vect) 462 | { 463 | SoftwareSerial::handle_interrupt(); 464 | } 465 | #endif 466 | 467 | // 468 | // Constructor 469 | // 470 | SoftwareSerial::SoftwareSerial(){ 471 | 472 | } 473 | 474 | SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : 475 | _rx_delay_centering(0), 476 | _rx_delay_intrabit(0), 477 | _rx_delay_stopbit(0), 478 | _tx_delay(0), 479 | _buffer_overflow(false), 480 | _inverse_logic(inverse_logic) 481 | { 482 | setTX(transmitPin); 483 | setRX(receivePin); 484 | } 485 | 486 | // 487 | // Destructor 488 | // 489 | SoftwareSerial::~SoftwareSerial() 490 | { 491 | end(); 492 | } 493 | 494 | void SoftwareSerial::setTX(uint8_t tx) 495 | { 496 | pinMode(tx, OUTPUT); 497 | digitalWrite(tx, HIGH); 498 | _transmitBitMask = digitalPinToBitMask(tx); 499 | uint8_t port = digitalPinToPort(tx); 500 | _transmitPortRegister = portOutputRegister(port); 501 | } 502 | 503 | void SoftwareSerial::setRX(uint8_t rx) 504 | { 505 | pinMode(rx, INPUT); 506 | if (!_inverse_logic) 507 | digitalWrite(rx, HIGH); // pullup for normal logic! 508 | _receivePin = rx; 509 | _receiveBitMask = digitalPinToBitMask(rx); 510 | uint8_t port = digitalPinToPort(rx); 511 | _receivePortRegister = portInputRegister(port); 512 | } 513 | 514 | // 515 | // Public methods 516 | // 517 | 518 | void SoftwareSerial::begin(long speed) 519 | { 520 | _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; 521 | 522 | for (unsigned i=0; i 36 | #include 37 | #include 38 | 39 | /****************************************************************************** 40 | * Definitions 41 | ******************************************************************************/ 42 | 43 | #define _SS_MAX_RX_BUFF 64 // RX buffer size 44 | #ifndef GCC_VERSION 45 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 46 | #endif 47 | 48 | #if defined(__MK20DX128__) || defined(__MK20DX256__) 49 | 50 | class SoftwareSerial : public Stream 51 | { 52 | public: 53 | SoftwareSerial(); 54 | SoftwareSerial(uint8_t rxPin, uint8_t txPin, bool inverse_logic = false); 55 | ~SoftwareSerial() { end(); } 56 | void begin(unsigned long speed); 57 | void end(); 58 | bool listen() { return true; } 59 | bool isListening() { return true; } 60 | bool overflow() { bool ret = buffer_overflow; buffer_overflow = false; return ret; } 61 | virtual int available(); 62 | virtual int read(); 63 | int peek(); 64 | virtual void flush(); 65 | virtual size_t write(uint8_t byte); 66 | using Print::write; 67 | private: 68 | HardwareSerial *port; 69 | uint32_t cycles_per_bit; 70 | volatile uint8_t *txreg; 71 | volatile uint8_t *rxreg; 72 | bool buffer_overflow; 73 | uint8_t txpin; 74 | uint8_t rxpin; 75 | }; 76 | 77 | #else 78 | class SoftwareSerial : public Stream 79 | { 80 | private: 81 | // per object data 82 | uint8_t _receivePin; 83 | uint8_t _receiveBitMask; 84 | volatile uint8_t *_receivePortRegister; 85 | uint8_t _transmitBitMask; 86 | volatile uint8_t *_transmitPortRegister; 87 | 88 | uint16_t _rx_delay_centering; 89 | uint16_t _rx_delay_intrabit; 90 | uint16_t _rx_delay_stopbit; 91 | uint16_t _tx_delay; 92 | 93 | uint16_t _buffer_overflow : 1; 94 | uint16_t _inverse_logic : 1; 95 | 96 | // static data 97 | static char _receive_buffer[_SS_MAX_RX_BUFF]; 98 | static volatile uint8_t _receive_buffer_tail; 99 | static volatile uint8_t _receive_buffer_head; 100 | static SoftwareSerial *active_object; 101 | 102 | // private methods 103 | void recv(); 104 | uint8_t rx_pin_read(); 105 | void tx_pin_write(uint8_t pin_state); 106 | void setTX(uint8_t transmitPin); 107 | void setRX(uint8_t receivePin); 108 | 109 | // private static method for timing 110 | static inline void tunedDelay(uint16_t delay); 111 | 112 | 113 | public: 114 | // public methods 115 | SoftwareSerial(); 116 | SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); 117 | ~SoftwareSerial(); 118 | void begin(long speed); 119 | bool listen(); 120 | void end(); 121 | bool isListening() { return this == active_object; } 122 | bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } 123 | int peek(); 124 | 125 | virtual size_t write(uint8_t byte); 126 | virtual int read(); 127 | virtual int available(); 128 | virtual void flush(); 129 | 130 | using Print::write; 131 | 132 | // public only for easy access by interrupt handlers 133 | static inline void handle_interrupt(); 134 | }; 135 | 136 | // Arduino 0012 workaround 137 | #undef int 138 | #undef char 139 | #undef long 140 | #undef byte 141 | #undef float 142 | #undef abs 143 | #undef round 144 | 145 | #endif 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /example/RoombaLibTesting/RoombaLibTesting.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | iRobotCreate2 roomba(true, 10,7,3); 4 | 5 | /* 6 | * RX connect to TX of other device 7 | * TX connect to RX of other device 8 | 9 | Note: 10 | Not all pins on the Mega and Mega 2560 support change interrupts, 11 | so only the following can be used for RX: 12 | 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69 13 | 14 | Not all pins on the Leonardo support change interrupts, 15 | so only the following can be used for RX: 16 | 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI). 17 | */ 18 | 19 | int totalDuration1 =0; 20 | int totalDuration2 =0; 21 | int totalDuration3 =0; 22 | 23 | 24 | void setup(){ 25 | Serial.begin(9600); 26 | //roomba.resetBaudRate(); 27 | delay(2000); 28 | roomba.start(); 29 | roomba.fullMode(); 30 | delay(1000); 31 | roomba.setPowerLEDs(128, 128); 32 | roomba.setDebrisLED(true); 33 | roomba.setSpotLED(true); 34 | roomba.setDockLED(true); 35 | roomba.setWarningLED(true); 36 | roomba.setScheduleLEDs(Monday + Wednesday, true, true, true, false, true); 37 | roomba.setDigitLEDFromASCII(1, 'A'); 38 | roomba.setDigitLEDFromASCII(2, 'B'); 39 | roomba.setDigitLEDFromASCII(3, 'C'); 40 | roomba.setDigitLEDFromASCII(4, 'D'); 41 | 42 | Notes songNotes[16]; 43 | songNotes[0].note = 76; 44 | songNotes[1].note = 76; 45 | songNotes[2].note = 76; 46 | songNotes[3].note = 76; 47 | songNotes[4].note = 76; 48 | songNotes[5].note = 76; 49 | songNotes[6].note = 76; 50 | songNotes[7].note = 79; 51 | songNotes[8].note = 72; 52 | songNotes[9].note = 74; 53 | songNotes[10].note = 76; 54 | 55 | 56 | songNotes[0].duration = 16; 57 | songNotes[1].duration = 16; 58 | songNotes[2].duration = 32; 59 | songNotes[3].duration = 16; 60 | songNotes[4].duration = 16; 61 | songNotes[5].duration = 32; 62 | songNotes[6].duration = 16; 63 | songNotes[7].duration = 16; 64 | songNotes[8].duration = 16; 65 | songNotes[9].duration = 16; 66 | songNotes[10].duration = 64; 67 | 68 | roomba.createSong(1, 11, songNotes); 69 | 70 | for(int i=0;i<11;i++){ 71 | totalDuration1 += songNotes[i].duration; 72 | } 73 | roomba.playSong(1); 74 | totalDuration1 = 100+1000*(totalDuration1/64.0); 75 | delay(totalDuration1); 76 | 77 | songNotes[0].note = 77; 78 | songNotes[1].note = 77; 79 | songNotes[2].note = 77; 80 | songNotes[3].note = 77; 81 | songNotes[4].note = 77; 82 | songNotes[5].note = 76; 83 | songNotes[6].note = 76; 84 | songNotes[7].note = 76; 85 | songNotes[8].note = 76; 86 | songNotes[9].note = 74; 87 | songNotes[10].note = 74; 88 | songNotes[11].note = 76; 89 | songNotes[12].note = 74; 90 | songNotes[13].note = 79; 91 | 92 | 93 | songNotes[0].duration = 16; 94 | songNotes[1].duration = 16; 95 | songNotes[2].duration = 16; 96 | songNotes[3].duration = 16; 97 | songNotes[4].duration = 16; 98 | songNotes[5].duration = 16; 99 | songNotes[6].duration = 16; 100 | songNotes[7].duration = 16; 101 | songNotes[8].duration = 16; 102 | songNotes[9].duration = 16; 103 | songNotes[10].duration = 16; 104 | songNotes[11].duration = 16; 105 | songNotes[12].duration = 32; 106 | songNotes[13].duration = 32; 107 | 108 | roomba.createSong(2, 14, songNotes); 109 | 110 | for(int i=0;i<14;i++){ 111 | totalDuration2 += songNotes[i].duration; 112 | } 113 | 114 | roomba.playSong(2); 115 | totalDuration2 = 100+1000*(totalDuration2/64.0); 116 | delay(totalDuration2); 117 | 118 | roomba.playSong(1); 119 | delay(totalDuration1); 120 | 121 | songNotes[0].note = 77; 122 | songNotes[1].note = 77; 123 | songNotes[2].note = 77; 124 | songNotes[3].note = 77; 125 | songNotes[4].note = 77; 126 | songNotes[5].note = 76; 127 | songNotes[6].note = 76; 128 | songNotes[7].note = 76; 129 | songNotes[8].note = 79; 130 | songNotes[9].note = 79; 131 | songNotes[10].note = 76; 132 | songNotes[11].note = 74; 133 | songNotes[12].note = 72; 134 | 135 | 136 | songNotes[0].duration = 16; 137 | songNotes[1].duration = 16; 138 | songNotes[2].duration = 16; 139 | songNotes[3].duration = 16; 140 | songNotes[4].duration = 16; 141 | songNotes[5].duration = 16; 142 | songNotes[6].duration = 16; 143 | songNotes[7].duration = 16; 144 | songNotes[8].duration = 16; 145 | songNotes[9].duration = 16; 146 | songNotes[10].duration = 16; 147 | songNotes[11].duration = 16; 148 | songNotes[12].duration = 64; 149 | 150 | roomba.createSong(3, 13, songNotes); 151 | 152 | for(int i=0;i<13;i++){ 153 | totalDuration3 += songNotes[i].duration; 154 | } 155 | 156 | roomba.playSong(3); 157 | totalDuration3 = 100+1000*(totalDuration3/64.0); 158 | delay(totalDuration3); 159 | //roomba.pushButton(Clean + Minute + Hour); 160 | 161 | //roomba.drive(100, 0); 162 | //delay(1000); 163 | //roomba.driveLeft(200); 164 | //delay(1000); 165 | //roomba.driveRight(200); 166 | //delay(1000); 167 | //roomba.drive(0,0); 168 | 169 | // delay(17000); 170 | // roomba.turnOff(); 171 | roomba.clean(); 172 | 173 | } 174 | 175 | void loop(){ 176 | delay(600000); 177 | roomba.fullMode(); 178 | delay(2000); 179 | roomba.playSong(1); 180 | delay(totalDuration1); 181 | roomba.playSong(2); 182 | delay(totalDuration2); 183 | roomba.playSong(1); 184 | delay(totalDuration1); 185 | roomba.playSong(3); 186 | delay(totalDuration3); 187 | delay(3000); 188 | roomba.clean(); 189 | } 190 | 191 | 192 | -------------------------------------------------------------------------------- /iRobotCreate2.cpp: -------------------------------------------------------------------------------- 1 | // iRobotCreate2.cpp 2 | // 3 | // Copyright (C) 2014 Dom Amato 4 | // MIT Licence 5 | 6 | #include "iRobotCreate2.h" 7 | 8 | iRobotCreate2::iRobotCreate2(){ 9 | usingSoftSerial = false; 10 | Serial.begin(19200); 11 | _mainBrushDirection = false; 12 | _sideBrushDirection = false; 13 | _mainBrush = false; 14 | _sideBrush = false; 15 | _vacuum = false; 16 | _mainBrushSpeed = 0; 17 | _sideBrushSpeed = 0; 18 | _vacuumSpeed = 0; 19 | _debrisLED = false; 20 | _spotLED = false; 21 | _dockLED = false; 22 | _warningLED = false; 23 | 24 | _isPaused = false; 25 | _isStreaming = false; 26 | 27 | _digit1 = 0; 28 | _digit2 = 0; 29 | _digit3 = 0; 30 | _digit4 = 0; 31 | } 32 | 33 | iRobotCreate2::iRobotCreate2(bool useSoftSerial, byte rxPin, byte txPin, byte baudRateChangePin){ 34 | usingSoftSerial = useSoftSerial; 35 | _brcPin = baudRateChangePin; 36 | 37 | if (useSoftSerial){ 38 | softSerial = SoftwareSerial(rxPin, txPin); 39 | softSerial.begin(19200); 40 | } 41 | else { 42 | Serial.begin(19200); 43 | } 44 | _mainBrushDirection = false; 45 | _sideBrushDirection = false; 46 | _mainBrush = false; 47 | _sideBrush = false; 48 | _vacuum = false; 49 | _mainBrushSpeed = 0; 50 | _sideBrushSpeed = 0; 51 | _vacuumSpeed = 0; 52 | _debrisLED = false; 53 | _spotLED = false; 54 | _dockLED = false; 55 | _warningLED = false; 56 | 57 | _isPaused = false; 58 | _isStreaming = false; 59 | 60 | _digit1 = 0; 61 | _digit2 = 0; 62 | _digit3 = 0; 63 | _digit4 = 0; 64 | } 65 | 66 | //available as passive safe or full modes 67 | void iRobotCreate2::start(){ 68 | if (usingSoftSerial){ 69 | softSerial.write(128); 70 | } 71 | else { 72 | Serial.write(128); 73 | } 74 | } 75 | void iRobotCreate2::reset(){ 76 | if (usingSoftSerial){ 77 | softSerial.write(7); 78 | } 79 | else { 80 | Serial.write(7); 81 | } 82 | } 83 | 84 | void iRobotCreate2::resetBaudRate(){ 85 | pinMode(_brcPin, OUTPUT); 86 | delay(2000); 87 | digitalWrite(13, HIGH); 88 | digitalWrite(_brcPin, HIGH); 89 | for (int i = 0; i < 3; i++){ 90 | digitalWrite(13, LOW); 91 | digitalWrite(_brcPin, LOW); 92 | delay(200); 93 | digitalWrite(13, HIGH); 94 | digitalWrite(_brcPin, HIGH); 95 | delay(200); 96 | } 97 | start(); 98 | } 99 | 100 | void iRobotCreate2::stop(){ 101 | if (usingSoftSerial){ 102 | softSerial.write(173); 103 | } 104 | else { 105 | Serial.write(173); 106 | } 107 | } 108 | void iRobotCreate2::baud(long rate){ 109 | byte baudCode = 7; 110 | switch (rate){ 111 | case 300: 112 | baudCode = 0; 113 | break; 114 | case 600: 115 | baudCode = 1; 116 | break; 117 | case 1200: 118 | baudCode = 2; 119 | break; 120 | case 2400: 121 | baudCode = 3; 122 | break; 123 | case 4800: 124 | baudCode = 4; 125 | break; 126 | case 9600: 127 | baudCode = 5; 128 | break; 129 | case 14400: 130 | baudCode = 6; 131 | break; 132 | case 19200: 133 | baudCode = 7; 134 | break; 135 | case 28800: 136 | baudCode = 8; 137 | break; 138 | case 38400: 139 | baudCode = 9; 140 | break; 141 | case 57600: 142 | baudCode = 10; 143 | break; 144 | case 115200: 145 | baudCode = 11; 146 | break; 147 | } 148 | if (usingSoftSerial){ 149 | softSerial.write(129); 150 | softSerial.write(baudCode); 151 | } 152 | else { 153 | Serial.write(129); 154 | Serial.write(baudCode); 155 | Serial.begin(rate); 156 | } 157 | } 158 | void iRobotCreate2::safeMode(){ 159 | if (usingSoftSerial){ 160 | softSerial.write(131); 161 | } 162 | else { 163 | Serial.write(131); 164 | } 165 | } 166 | void iRobotCreate2::fullMode(){ 167 | if (usingSoftSerial){ 168 | softSerial.write(132); 169 | } 170 | else { 171 | Serial.write(132); 172 | } 173 | } 174 | void iRobotCreate2::passiveMode(){ 175 | start(); 176 | } 177 | void iRobotCreate2::clean(){ 178 | if (usingSoftSerial){ 179 | softSerial.write(135); 180 | } 181 | else { 182 | Serial.write(135); 183 | } 184 | } 185 | void iRobotCreate2::maxClean(){ 186 | if (usingSoftSerial){ 187 | softSerial.write(136); 188 | } 189 | else { 190 | Serial.write(136); 191 | } 192 | } 193 | void iRobotCreate2::spotClean(){ 194 | if (usingSoftSerial){ 195 | softSerial.write(134); 196 | } 197 | else { 198 | Serial.write(134); 199 | } 200 | } 201 | void iRobotCreate2::seekDock(){ 202 | if (usingSoftSerial){ 203 | softSerial.write(143); 204 | } 205 | else { 206 | Serial.write(143); 207 | } 208 | } 209 | void iRobotCreate2::turnOff(){ 210 | if (usingSoftSerial){ 211 | softSerial.write(133); 212 | } 213 | else { 214 | Serial.write(133); 215 | } 216 | } 217 | void iRobotCreate2::schedule(byte days, byte hour[], byte minute[]){ 218 | if (usingSoftSerial){ 219 | softSerial.write(167); 220 | softSerial.write(days); 221 | int timeCounter = 0; 222 | for (int i = 0; i < 8; i++){ 223 | if (CHECK_BIT(days, i)){ 224 | softSerial.write(hour[timeCounter]); 225 | softSerial.write(minute[timeCounter++]); 226 | } 227 | else { 228 | softSerial.write((byte)0x00); 229 | softSerial.write((byte)0x00); 230 | } 231 | } 232 | } 233 | else { 234 | Serial.write(167); 235 | Serial.write(days); 236 | int timeCounter = 0; 237 | for (int i = 0; i < 8; i++){ 238 | if (CHECK_BIT(days, i)){ 239 | Serial.write(hour[timeCounter]); 240 | Serial.write(minute[timeCounter++]); 241 | } 242 | else { 243 | Serial.write((byte)0x00); 244 | Serial.write((byte)0x00); 245 | } 246 | } 247 | } 248 | } 249 | void iRobotCreate2::setTime(SetDayOfWeek day, byte hour, byte minute){ 250 | if (usingSoftSerial){ 251 | softSerial.write(168); 252 | softSerial.write(day); 253 | softSerial.write(hour); 254 | softSerial.write(minute); 255 | 256 | } 257 | else { 258 | Serial.write(168); 259 | Serial.write(day); 260 | 261 | Serial.write(hour); 262 | Serial.write(minute); 263 | 264 | } 265 | } 266 | void iRobotCreate2::pushButton(byte button){ 267 | if (usingSoftSerial){ 268 | softSerial.write(165); 269 | softSerial.write(button); 270 | } 271 | else { 272 | Serial.write(165); 273 | Serial.write(button); 274 | } 275 | } 276 | //play songs 277 | void iRobotCreate2::createSong(byte songNumber, byte numberOfNotes, Notes notes[]){ 278 | if (usingSoftSerial){ 279 | softSerial.write(140); 280 | softSerial.write(songNumber); 281 | softSerial.write(numberOfNotes); 282 | for (int i = 0; i < numberOfNotes; i++){ 283 | softSerial.write(notes[i].note); 284 | softSerial.write(notes[i].duration); 285 | } 286 | } 287 | else { 288 | Serial.write(140); 289 | Serial.write(songNumber); 290 | Serial.write(numberOfNotes); 291 | for (int i = 0; i < numberOfNotes; i++){ 292 | Serial.write(notes[i].note); 293 | Serial.write(notes[i].duration); 294 | } 295 | } 296 | } 297 | 298 | void iRobotCreate2::playSong(byte songNumber){ 299 | if (usingSoftSerial){ 300 | softSerial.write(141); 301 | softSerial.write(songNumber); 302 | } 303 | else { 304 | Serial.write(141); 305 | Serial.write(songNumber); 306 | } 307 | } 308 | 309 | 310 | //available is safe or full modes 311 | //wheel motors 312 | void iRobotCreate2::drive(int velocity, int radius){ 313 | clamp(velocity, -500, 500); 314 | clamp(radius, -2000, 2000); 315 | if (usingSoftSerial){ 316 | softSerial.write(137); 317 | softSerial.write(velocity >> 8); 318 | softSerial.write(velocity); 319 | softSerial.write(radius >> 8); 320 | softSerial.write(radius); 321 | } 322 | else { 323 | Serial.write(137); 324 | Serial.write(velocity >> 8); 325 | Serial.write(velocity); 326 | Serial.write(radius >> 8); 327 | Serial.write(radius); 328 | } 329 | } 330 | 331 | void iRobotCreate2::turnCW(unsigned short velocity, unsigned short degrees){ 332 | drive(velocity, -1); 333 | clamp(velocity, 0, 500); 334 | delay(2700); 335 | //delay((1580 + 2.25*velocity)/velocity*degrees); 336 | //delay((-0.03159720835 * velocity + 21.215270835) * degrees); 337 | drive(0,0); 338 | } 339 | void iRobotCreate2::turnCCW(unsigned short velocity, unsigned short degrees){ 340 | drive(velocity, 1); 341 | clamp(velocity, 0, 500); 342 | delay(6600); 343 | //delay(2708.3333/velocity*degrees); 344 | //delay((1580 + 2.25*velocity)/velocity*degrees); 345 | //delay((-0.03159720835 * velocity + 21.215270835) * degrees); 346 | drive(0,0); 347 | } 348 | 349 | void iRobotCreate2::driveWheels(int right, int left){ 350 | clamp(right, -500, 500); 351 | clamp(left, -500, 500); 352 | if (usingSoftSerial){ 353 | softSerial.write(145); 354 | softSerial.write(right >> 8); 355 | softSerial.write(right); 356 | softSerial.write(left >> 8); 357 | softSerial.write(left); 358 | } 359 | else { 360 | Serial.write(145); 361 | Serial.write(right >> 8); 362 | Serial.write(right); 363 | Serial.write(left >> 8); 364 | Serial.write(left); 365 | } 366 | } 367 | void iRobotCreate2::driveLeft(int left){ 368 | driveWheels(left, 0); 369 | } 370 | void iRobotCreate2::driveRight(int right){ 371 | driveWheels(0, right); 372 | } 373 | void iRobotCreate2::driveWheelsPWM(int rightPWM, int leftPWM){ 374 | clamp(rightPWM, -255, 255); 375 | clamp(leftPWM, -255, 255); 376 | if (usingSoftSerial){ 377 | softSerial.write(146); 378 | softSerial.write(rightPWM >> 8); 379 | softSerial.write(rightPWM); 380 | softSerial.write(leftPWM >> 8); 381 | softSerial.write(leftPWM); 382 | } 383 | else { 384 | Serial.write(146); 385 | Serial.write(rightPWM >> 8); 386 | Serial.write(rightPWM); 387 | Serial.write(leftPWM >> 8); 388 | Serial.write(leftPWM); 389 | } 390 | } 391 | //cleaning motors 392 | void iRobotCreate2::enableBrushes(bool mainBrushDirection, bool sideBrushDirection, bool mainBrush, bool vacuum, bool sideBrush){ 393 | 394 | byte motorsEnabled = (mainBrushDirection ? 16 : 0) + (sideBrushDirection ? 8 : 0) + (mainBrush ? 4 : 0) + (vacuum ? 2 : 0) + (sideBrush ? 1 : 0); 395 | if (usingSoftSerial){ 396 | softSerial.write(138); 397 | softSerial.write(motorsEnabled); 398 | } 399 | else { 400 | Serial.write(138); 401 | Serial.write(motorsEnabled); 402 | } 403 | } 404 | void iRobotCreate2::setMainBrush(bool direction, bool enable){ 405 | enableBrushes(direction, _sideBrushDirection, enable, _vacuum, _sideBrush); 406 | } 407 | void iRobotCreate2::setSideBrush(bool direction, bool enable){ 408 | enableBrushes(_mainBrushDirection, direction, _mainBrush, _vacuum, enable); 409 | } 410 | void iRobotCreate2::enableVacuum(bool enable){ 411 | enableBrushes(_mainBrushDirection, _sideBrushDirection, _mainBrush, enable, _sideBrush); 412 | } 413 | void iRobotCreate2::setMainBrushSpeed(char speed){ 414 | _mainBrushSpeed = speed; 415 | if (usingSoftSerial){ 416 | softSerial.write(144); 417 | softSerial.write((byte)speed); 418 | softSerial.write((byte)_sideBrushSpeed); 419 | softSerial.write((byte)_vacuumSpeed); 420 | } 421 | else { 422 | Serial.write(144); 423 | Serial.write((byte)speed); 424 | Serial.write((byte)_sideBrushSpeed); 425 | Serial.write((byte)_vacuumSpeed); 426 | } 427 | } 428 | void iRobotCreate2::setSideBrushSpeed(char speed){ 429 | _sideBrushSpeed = speed; 430 | if (usingSoftSerial){ 431 | softSerial.write(144); 432 | softSerial.write((byte)_mainBrushSpeed); 433 | softSerial.write((byte)speed); 434 | softSerial.write((byte)_vacuumSpeed); 435 | } 436 | else { 437 | Serial.write(144); 438 | Serial.write((byte)_mainBrushSpeed); 439 | Serial.write((byte)speed); 440 | Serial.write((byte)_vacuumSpeed); 441 | } 442 | } 443 | void iRobotCreate2::setVaccumSpeed(char speed){ 444 | _vacuumSpeed = speed; 445 | if (usingSoftSerial){ 446 | softSerial.write(144); 447 | softSerial.write((byte)_mainBrushSpeed); 448 | softSerial.write((byte)_sideBrushSpeed); 449 | softSerial.write((byte)speed); 450 | } 451 | else { 452 | Serial.write(144); 453 | Serial.write((byte)_mainBrushSpeed); 454 | Serial.write((byte)_sideBrushSpeed); 455 | Serial.write((byte)speed); 456 | } 457 | } 458 | //leds 459 | void iRobotCreate2::setPowerLEDs(byte color, byte intensity){ 460 | _color = color; 461 | _intensity = intensity; 462 | if (usingSoftSerial){ 463 | softSerial.write(139); 464 | softSerial.write((byte)0x00); 465 | softSerial.write((byte)_color); 466 | softSerial.write((byte)_intensity); 467 | } 468 | else { 469 | Serial.write(139); 470 | Serial.write((byte)0x00); 471 | Serial.write((byte)_color); 472 | Serial.write((byte)_intensity); 473 | } 474 | } 475 | void iRobotCreate2::setDebrisLED(bool enable){ 476 | _debrisLED = enable; 477 | if (usingSoftSerial){ 478 | softSerial.write(139); 479 | softSerial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 480 | softSerial.write((byte)_color); 481 | softSerial.write((byte)_intensity); 482 | } 483 | else { 484 | Serial.write(139); 485 | Serial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 486 | Serial.write((byte)_color); 487 | Serial.write((byte)_intensity); 488 | } 489 | } 490 | void iRobotCreate2::setSpotLED(bool enable){ 491 | _spotLED = enable; 492 | if (usingSoftSerial){ 493 | softSerial.write(139); 494 | softSerial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 495 | softSerial.write((byte)_color); 496 | softSerial.write((byte)_intensity); 497 | } 498 | else { 499 | Serial.write(139); 500 | Serial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 501 | Serial.write((byte)_color); 502 | Serial.write((byte)_intensity); 503 | } 504 | } 505 | void iRobotCreate2::setDockLED(bool enable){ 506 | _dockLED = enable; 507 | if (usingSoftSerial){ 508 | softSerial.write(139); 509 | softSerial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 510 | softSerial.write((byte)_color); 511 | softSerial.write((byte)_intensity); 512 | } 513 | else { 514 | Serial.write(139); 515 | Serial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 516 | Serial.write((byte)_color); 517 | Serial.write((byte)_intensity); 518 | } 519 | } 520 | void iRobotCreate2::setWarningLED(bool enable){ 521 | _warningLED = enable; 522 | if (usingSoftSerial){ 523 | softSerial.write(139); 524 | softSerial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 525 | softSerial.write((byte)_color); 526 | softSerial.write((byte)_intensity); 527 | } 528 | else { 529 | Serial.write(139); 530 | Serial.write((_debrisLED ? 1 : 0) + (_spotLED ? 2 : 0) + (_dockLED ? 4 : 0) + (_warningLED ? 8 : 0)); 531 | Serial.write((byte)_color); 532 | Serial.write((byte)_intensity); 533 | } 534 | } 535 | void iRobotCreate2::setScheduleLEDs(byte days, bool schedule, bool clock, bool am, bool pm, bool colon){ 536 | if (usingSoftSerial){ 537 | softSerial.write(162); 538 | softSerial.write(days); 539 | softSerial.write((colon ? 1 : 0) + (pm ? 2 : 0) + (am ? 4 : 0) + (clock ? 8 : 0) + (schedule ? 16 : 0)); 540 | } 541 | else { 542 | Serial.write(162); 543 | Serial.write(days); 544 | Serial.write((colon ? 1 : 0) + (pm ? 2 : 0) + (am ? 4 : 0) + (clock ? 8 : 0) + (schedule ? 16 : 0)); 545 | } 546 | } 547 | void iRobotCreate2::setDigitLEDs(byte digit1, byte digit2, byte digit3, byte digit4){ 548 | if (usingSoftSerial){ 549 | softSerial.write(163); 550 | softSerial.write(digit1); 551 | softSerial.write(digit2); 552 | softSerial.write(digit3); 553 | softSerial.write(digit4); 554 | } 555 | else { 556 | Serial.write(163); 557 | Serial.write(digit1); 558 | Serial.write(digit2); 559 | Serial.write(digit3); 560 | Serial.write(digit4); 561 | } 562 | } 563 | void iRobotCreate2::setDigitLEDFromASCII(byte digit, char letter){ 564 | switch (digit){ 565 | case 1: 566 | _digit1 = letter; 567 | break; 568 | case 2: 569 | _digit2 = letter; 570 | break; 571 | case 3: 572 | _digit3 = letter; 573 | break; 574 | case 4: 575 | _digit4 = letter; 576 | break; 577 | } 578 | if (usingSoftSerial){ 579 | softSerial.write(164); 580 | softSerial.write(_digit1); 581 | softSerial.write(_digit2); 582 | softSerial.write(_digit3); 583 | softSerial.write(_digit4); 584 | } 585 | else { 586 | Serial.write(164); 587 | Serial.write(digit); 588 | Serial.write(_digit1); 589 | Serial.write(_digit2); 590 | Serial.write(_digit3); 591 | Serial.write(_digit4); 592 | } 593 | } 594 | 595 | //non blocking sensor functions, serial data has to be decrypted outside of library or in separate function 596 | void iRobotCreate2::requestSensorData(byte sensorID) { 597 | byte packetID = 0; 598 | if (sensorID > 100){ 599 | switch (sensorID){ 600 | case 101: 601 | case 102: 602 | case 103: 603 | case 104: 604 | packetID = 7; 605 | break; 606 | case 105: 607 | case 106: 608 | case 107: 609 | case 108: 610 | packetID = 14; 611 | break; 612 | case 109: 613 | case 110: 614 | case 111: 615 | case 112: 616 | case 113: 617 | case 114: 618 | case 115: 619 | case 116: 620 | packetID = 18; 621 | break; 622 | case 117: 623 | case 118: 624 | case 119: 625 | case 120: 626 | case 121: 627 | case 122: 628 | packetID = 45; 629 | break; 630 | } 631 | 632 | } 633 | else { 634 | packetID = sensorID; 635 | } 636 | if (usingSoftSerial){ 637 | softSerial.write(142); 638 | softSerial.write(packetID); 639 | } 640 | else { 641 | Serial.write(142); 642 | Serial.write(packetID); 643 | } 644 | 645 | } 646 | void iRobotCreate2::requestSensorDataList(byte numOfRequests, byte requestIDs[]) { 647 | byte packetIDs[numOfRequests]; 648 | for (int i = 0; i 100){ 650 | switch (requestIDs[i]){ 651 | case 101: 652 | case 102: 653 | case 103: 654 | case 104: 655 | packetIDs[i] = 7; 656 | break; 657 | case 105: 658 | case 106: 659 | case 107: 660 | case 108: 661 | packetIDs[i] = 14; 662 | break; 663 | case 109: 664 | case 110: 665 | case 111: 666 | case 112: 667 | case 113: 668 | case 114: 669 | case 115: 670 | case 116: 671 | packetIDs[i] = 18; 672 | break; 673 | case 117: 674 | case 118: 675 | case 119: 676 | case 120: 677 | case 121: 678 | case 122: 679 | packetIDs[i] = 45; 680 | break; 681 | } 682 | 683 | } 684 | else { 685 | packetIDs[i] = requestIDs[i]; 686 | } 687 | } 688 | if (usingSoftSerial){ 689 | softSerial.write(149); 690 | softSerial.write(numOfRequests); 691 | for (int i = 0; i < numOfRequests; i++){ 692 | softSerial.write(packetIDs[i]); 693 | } 694 | } 695 | else { 696 | Serial.write(149); 697 | Serial.write(numOfRequests); 698 | for (int i = 0; i < numOfRequests; i++){ 699 | Serial.write(packetIDs[i]); 700 | } 701 | } 702 | 703 | } 704 | void iRobotCreate2::beginDataStream(byte numOfRequests, byte requestIDs[]){ 705 | byte packetIDs[numOfRequests]; 706 | for (int i = 0; i 100){ 708 | switch (requestIDs[i]){ 709 | case 101: 710 | case 102: 711 | case 103: 712 | case 104: 713 | packetIDs[i] = 7; 714 | break; 715 | case 105: 716 | case 106: 717 | case 107: 718 | case 108: 719 | packetIDs[i] = 14; 720 | break; 721 | case 109: 722 | case 110: 723 | case 111: 724 | case 112: 725 | case 113: 726 | case 114: 727 | case 115: 728 | case 116: 729 | packetIDs[i] = 18; 730 | break; 731 | case 117: 732 | case 118: 733 | case 119: 734 | case 120: 735 | case 121: 736 | case 122: 737 | packetIDs[i] = 45; 738 | break; 739 | } 740 | 741 | } 742 | else { 743 | packetIDs[i] = requestIDs[i]; 744 | } 745 | } 746 | if (usingSoftSerial){ 747 | softSerial.write(148); 748 | softSerial.write(numOfRequests); 749 | for (int i = 0; i < numOfRequests; i++){ 750 | softSerial.write(packetIDs[i]); 751 | } 752 | } 753 | else { 754 | Serial.write(148); 755 | Serial.write(numOfRequests); 756 | for (int i = 0; i < numOfRequests; i++){ 757 | Serial.write(packetIDs[i]); 758 | } 759 | } 760 | 761 | } 762 | void iRobotCreate2::pauseStream(){ 763 | if (usingSoftSerial){ 764 | softSerial.write(150); 765 | softSerial.write((byte)0x00); 766 | } 767 | else { 768 | Serial.write(150); 769 | Serial.write((byte)0x00); 770 | } 771 | } 772 | void iRobotCreate2::resumeSteam(){ 773 | if (usingSoftSerial){ 774 | softSerial.write(150); 775 | softSerial.write(1); 776 | } 777 | else { 778 | Serial.write(150); 779 | Serial.write(1); 780 | } 781 | } 782 | 783 | //blocking sensor functions - these will request data and wait until a response is recieved, then return the response 784 | 785 | /** 786 | Returns the value from the requested id 787 | **/ 788 | 789 | int iRobotCreate2::getSensorData(byte sensorID){ 790 | int returnVal; 791 | byte packetID = 0; 792 | if (sensorID > 100){ 793 | switch (sensorID){ 794 | case 101: 795 | case 102: 796 | case 103: 797 | case 104: 798 | packetID = 7; 799 | break; 800 | case 105: 801 | case 106: 802 | case 107: 803 | case 108: 804 | packetID = 14; 805 | break; 806 | case 109: 807 | case 110: 808 | case 111: 809 | case 112: 810 | case 113: 811 | case 114: 812 | case 115: 813 | case 116: 814 | packetID = 18; 815 | break; 816 | case 117: 817 | case 118: 818 | case 119: 819 | case 120: 820 | case 121: 821 | case 122: 822 | packetID = 45; 823 | break; 824 | } 825 | 826 | } 827 | else { 828 | packetID = sensorID; 829 | } 830 | byte MSB = 0; 831 | byte LSB = 0; 832 | if (usingSoftSerial){ 833 | softSerial.write(142); 834 | softSerial.write(packetID); 835 | if(is_in_array(packetID)){ 836 | while (!softSerial.available()); 837 | returnVal = softSerial.read(); 838 | } else { 839 | while (!softSerial.available()); 840 | MSB = softSerial.read(); 841 | LSB = softSerial.read(); 842 | returnVal = (MSB << 7) | LSB; 843 | } 844 | 845 | } 846 | else { 847 | Serial.write(142); 848 | Serial.write(packetID); 849 | if(is_in_array(packetID)){ 850 | while (!Serial.available()); 851 | returnVal = Serial.read(); 852 | } else { 853 | while (!Serial.available()); 854 | MSB = Serial.read(); 855 | LSB = Serial.read(); 856 | returnVal = (MSB << 7) | LSB; 857 | } 858 | } 859 | return returnVal; 860 | } 861 | 862 | /** 863 | Returns an array of values containing all the ids 864 | **/ 865 | 866 | int * iRobotCreate2::getSensorData(byte numOfRequests, byte requestIDs[]){ 867 | int returnVal[numOfRequests]; 868 | byte packetIDs[numOfRequests]; 869 | for (int i = 0; i 100){ 871 | switch (requestIDs[i]){ 872 | case 101: 873 | case 102: 874 | case 103: 875 | case 104: 876 | packetIDs[i] = 7; 877 | break; 878 | case 105: 879 | case 106: 880 | case 107: 881 | case 108: 882 | packetIDs[i] = 14; 883 | break; 884 | case 109: 885 | case 110: 886 | case 111: 887 | case 112: 888 | case 113: 889 | case 114: 890 | case 115: 891 | case 116: 892 | packetIDs[i] = 18; 893 | break; 894 | case 117: 895 | case 118: 896 | case 119: 897 | case 120: 898 | case 121: 899 | case 122: 900 | packetIDs[i] = 45; 901 | break; 902 | } 903 | 904 | } 905 | else { 906 | packetIDs[i] = requestIDs[i]; 907 | } 908 | } 909 | if (usingSoftSerial){ 910 | softSerial.write(149); 911 | softSerial.write(numOfRequests); 912 | for (int i = 0; i < numOfRequests; i++){ 913 | softSerial.write(packetIDs[i]); 914 | } 915 | } 916 | else { 917 | Serial.write(149); 918 | Serial.write(numOfRequests); 919 | for (int i = 0; i < numOfRequests; i++){ 920 | Serial.write(packetIDs[i]); 921 | } 922 | } 923 | byte MSB = 0; 924 | byte LSB = 0; 925 | byte pos = 0; 926 | if (usingSoftSerial){ 927 | while (softSerial.available()){ 928 | MSB = softSerial.read(); 929 | LSB = softSerial.read(); 930 | returnVal[pos++] = (MSB << 7) | LSB; 931 | } 932 | } 933 | else { 934 | while (Serial.available()){ 935 | MSB = Serial.read(); 936 | LSB = Serial.read(); 937 | returnVal[pos++] = (MSB << 7) | LSB; 938 | } 939 | } 940 | 941 | return returnVal; 942 | } 943 | 944 | /** 945 | Returns false if failed, due to a timeout 946 | returns true if successful and fills the buffer provided by the pointer 947 | **/ 948 | bool iRobotCreate2::getSensorData(byte * buffer, byte bufferLength) 949 | { 950 | while (bufferLength-- > 0) 951 | { 952 | unsigned long startTime = millis(); 953 | if (usingSoftSerial){ 954 | while (!softSerial.available()) 955 | { 956 | // Look for a timeout 957 | if (millis() > startTime + 200) 958 | return false; // Timed out 959 | } 960 | *buffer++ = softSerial.read(); 961 | } 962 | else { 963 | while (!Serial.available()) 964 | { 965 | // Look for a timeout 966 | if (millis() > startTime + 200) 967 | return false; // Timed out 968 | } 969 | *buffer++ = Serial.read(); 970 | } 971 | } 972 | return true; 973 | } 974 | 975 | bool iRobotCreate2::is_in_array(byte val){ 976 | for (int i=0;i<22;i++){ 977 | if (val == single_byte_packets[i]){ 978 | return true; 979 | } 980 | } 981 | return false; 982 | } -------------------------------------------------------------------------------- /iRobotCreate2.h: -------------------------------------------------------------------------------- 1 | #ifndef IROBOTCREATE2_H 2 | #define IROBOTCREATE2_H 3 | 4 | // Arduino versioning. 5 | #if defined(ARDUINO) && ARDUINO >= 100 6 | #include "Arduino.h" 7 | #else 8 | #include "WProgram.h" 9 | #endif 10 | #include "SoftwareSerial.h" 11 | 12 | #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) 13 | 14 | #define clamp(value, min, max) (value < min ? min : value > max ? max : value) 15 | 16 | enum ScheduleDayOfWeek { 17 | Sunday = 1, 18 | Monday = 2, 19 | Tuesday = 4, 20 | Wednesday = 8, 21 | Thursday = 16, 22 | Friday = 32, 23 | Saturday = 64 24 | }; 25 | 26 | enum SetDayOfWeek { 27 | Sun = 0, 28 | Mon = 1, 29 | Tues = 2, 30 | Weds = 3, 31 | Thurs = 4, 32 | Fri = 5, 33 | Sat = 6 34 | }; 35 | 36 | enum iRobotButtons { 37 | Clean = 1, 38 | Spot = 2, 39 | Dock = 4, 40 | Minute = 8, 41 | Hour = 16, 42 | Day = 32, 43 | Schedule = 64, 44 | Clock = 128 45 | }; 46 | 47 | struct Notes { 48 | byte note; 49 | byte duration; 50 | }; 51 | 52 | enum Drive 53 | { 54 | DriveStraight = 0x8000, 55 | DriveInPlaceClockwise = 0xFFFF, 56 | DriveInPlaceCounterClockwise = 0x0001, 57 | }; 58 | 59 | enum ChargeState 60 | { 61 | ChargeStateNotCharging = 0, 62 | ChargeStateReconditioningCharging = 1, 63 | ChargeStateFullChanrging = 2, 64 | ChargeStateTrickleCharging = 3, 65 | ChargeStateWaiting = 4, 66 | ChargeStateFault = 5, 67 | }; 68 | 69 | enum packetIds { 70 | //all sent within packet 7, need to unpack byte upon retrival 71 | rightBumper = 101, 72 | leftBumper = 102, 73 | rightWheelDrop = 103, 74 | leftWheelDrop = 104, 75 | //all sent within packet 14, need to unpack byte upon retrival 76 | sideBrushOvercurrent = 105, 77 | mainBrushOvercurrent = 106, 78 | rightWheelOvercurrent = 107, 79 | leftWheelOvercurrent = 108, 80 | //all sent within packet 18, need to unpack byte upon retrival 81 | cleanButton = 109, 82 | spotButton = 110, 83 | dockButton = 111, 84 | minuteButton = 112, 85 | hourButton = 113, 86 | dayButton = 114, 87 | scheduleButton = 115, 88 | clockButton = 116, 89 | //all sent within packet 45, need to unpack byte upon retrival 90 | lightBumperLeft = 117, 91 | lightBumperFrontLeft = 118, 92 | lightBumperCenterLeft = 119, 93 | lightBumperCenterRight = 120, 94 | lightBumperFrontRight = 121, 95 | lightBumperRight = 122, 96 | //boolean values 97 | wallSensor = 8, 98 | leftCliff = 9, 99 | frontLeftCliff = 10, 100 | frontRightCliff = 11, 101 | rightCliff = 12, 102 | virtualWall = 13, 103 | songPlaying = 37, 104 | stasis = 58, 105 | //byte values 106 | dirtDetector = 15, 107 | irOmnidirectional = 17, 108 | irLeft = 52, 109 | irRight = 53, 110 | chargingState = 21, //0-5 111 | chargingSources = 34, //0-3 112 | operationalMode = 35, //0-3 113 | songNumber = 36, //0-15 114 | streamPackets = 38, //0-108 115 | //char values 116 | batteryTempInC = 24, 117 | //int values 118 | distanceTraveled = 19, 119 | degreesTurned = 20, 120 | currentFlow = 23, 121 | wallSignal = 27, //0-1027 122 | leftCliffSignal = 28, //0-4095 123 | frontLeftCliffSignal = 29, //0-4095 124 | frontRightCliffSignal = 30, //0-4095 125 | rightCliffSignal = 31, //0-4095 126 | requestedVelocity = 39, //-500 - 500 127 | requestedRadius = 40, 128 | requestedRightVelocity = 41, //-500 - 500 129 | requestedLeftVelocity = 42, //-500 - 500 130 | lightBumperLeftSignal = 46, //0-4095 131 | lightBumperFrontLeftSignal = 47, //0-4095 132 | lightBumperCenterLeftSignal = 48, //0-4095 133 | lightBumperCenterRightSignal = 49, //0-4095 134 | lightBumperFrontRightSignal = 50, //0-4095 135 | lightBumperRightSignal = 51, //0-4095 136 | leftMotorCurrent = 54, 137 | rightMotorCurrent = 55, 138 | mainBrushCurrent = 56, 139 | sideBrushCurrent = 57, 140 | //unsigned int values 141 | batteryVoltage = 22, 142 | batterCharge = 25, 143 | batteryCapacity = 26, 144 | leftEncoderCount = 43, 145 | rightEncoderCount = 44 146 | 147 | }; 148 | 149 | enum notes{ 150 | N_G1 = 31, 151 | N_G1S = 32, 152 | N_A1 = 33, 153 | N_A1S = 34, 154 | N_B1 = 35, 155 | N_C1 = 36, 156 | N_C1S = 37, 157 | N_D1 = 38, 158 | N_D1S = 39, 159 | N_E1 = 40, 160 | N_F1 = 41, 161 | N_F1S = 42, 162 | N_G2 = 43, 163 | N_G2S = 44, 164 | N_A2 = 45, 165 | N_A2S = 46, 166 | N_B2 = 47, 167 | N_C2 = 48, 168 | N_C2S = 49, 169 | N_D2 = 50, 170 | N_D2S = 51, 171 | N_E2 = 52, 172 | N_F2 = 53, 173 | N_F2S = 54, 174 | N_G3 = 55, 175 | N_G3S = 56, 176 | N_A3 = 57, 177 | N_A3S = 58, 178 | N_B3 = 59, 179 | N_C3 = 60, 180 | N_C3S = 61, 181 | N_D3 = 62, 182 | N_D3S = 63, 183 | N_E3 = 64, 184 | N_F3 = 65, 185 | N_F3S = 66, 186 | N_G4 = 67, 187 | N_G4S = 68, 188 | N_A4 = 69, 189 | N_A4S = 70, 190 | N_B4 = 71, 191 | N_C4 = 72, 192 | N_C4S = 73, 193 | N_D4 = 74, 194 | N_D4S = 75, 195 | N_E4 = 76, 196 | N_F4 = 77, 197 | N_F4S = 78, 198 | N_G5 = 79, 199 | N_G5S = 80, 200 | N_A5 = 81, 201 | N_A5S = 82, 202 | N_B5 = 83, 203 | N_C5 = 84, 204 | N_C5S = 85, 205 | N_D5 = 86, 206 | N_D5S = 87, 207 | N_E5 = 88, 208 | N_F5 = 89, 209 | N_F5S = 90, 210 | N_G6 = 91, 211 | N_G6S = 92, 212 | N_A6 = 93, 213 | N_A6S = 94, 214 | N_B6 = 95, 215 | N_C6 = 96, 216 | N_C6S = 97, 217 | N_D6 = 98, 218 | N_D6S = 99, 219 | N_E6 = 100, 220 | N_F6 = 101, 221 | N_F6S = 102, 222 | N_G7 = 103, 223 | N_G7S = 104, 224 | N_A7 = 105, 225 | N_A7S = 106, 226 | N_B7 = 107 227 | }; 228 | 229 | 230 | class iRobotCreate2 { 231 | public: 232 | 233 | iRobotCreate2(); 234 | iRobotCreate2(bool useSoftSerial, byte rxPin, byte txPin, byte baudRateChangePin); 235 | 236 | //available as passive safe or full modes 237 | void start(); 238 | void reset(); 239 | void stop(); 240 | void resetBaudRate(); 241 | void baud(long rate); 242 | void safeMode(); 243 | void fullMode(); 244 | void passiveMode(); 245 | void clean(); 246 | void maxClean(); 247 | void spotClean(); 248 | void seekDock(); 249 | void turnOff(); 250 | void schedule(byte days, byte hour[], byte minute[]); 251 | void setTime(SetDayOfWeek day, byte hour, byte minute); 252 | void pushButton(byte button); 253 | 254 | void createSong(byte songNumber, byte numberOfNotes, Notes notes[]); 255 | void playSong(byte songNumber); 256 | 257 | 258 | //available is safe or full modes 259 | //wheel motors 260 | void drive(int velocity, int radius); 261 | void driveWheels(int right, int left); 262 | void driveLeft(int left); 263 | void driveRight(int right); 264 | void driveWheelsPWM(int rightPWM, int leftPWM); 265 | void turnCW(unsigned short velocity,unsigned short degrees); 266 | void turnCCW(unsigned short velocity, unsigned short degrees); 267 | //cleaning motors 268 | void enableBrushes(bool mainBrushDirection, bool sideBrushDirection, bool mainBrush, bool vacuum, bool sideBrush); 269 | void setMainBrush(bool direction, bool enable); 270 | void setSideBrush(bool direction, bool enable); 271 | void enableVacuum(bool enable); 272 | void setMainBrushSpeed(char speed); 273 | void setSideBrushSpeed(char speed); 274 | void setVaccumSpeed(char speed); 275 | //leds 276 | void setPowerLEDs(byte color, byte intensity); 277 | void setDebrisLED(bool enable); 278 | void setSpotLED(bool enable); 279 | void setDockLED(bool enable); 280 | void setWarningLED(bool enable); 281 | void setScheduleLEDs(byte days, bool schedule, bool clock, bool am, bool pm, bool colon); 282 | void setDigitLEDs(byte digit1, byte digit2, byte digit3, byte digit4); 283 | void setDigitLEDFromASCII(byte digit, char letter); 284 | 285 | 286 | //non blocking sensor functions 287 | void requestSensorData(byte sensorID); 288 | void requestSensorDataList(byte numOfRequests, byte requestIDs[]); 289 | void beginDataStream(byte numOfRequests, byte requestIDs[]); 290 | void pauseStream(); 291 | void resumeSteam(); 292 | 293 | //blocking sensor functions - these will request data and wait until a response is recieved, then return the response 294 | int getSensorData(byte sensorID); 295 | int * getSensorData(byte numOfRequests, byte requestIDs[]); 296 | 297 | void decodeIR(byte value); 298 | bool getSensorData(byte * buffer, byte bufferLength); 299 | 300 | 301 | protected: 302 | byte _color; 303 | byte _intensity; 304 | byte _brcPin; 305 | bool _mainBrushDirection; 306 | bool _sideBrushDirection; 307 | bool _mainBrush; 308 | bool _sideBrush; 309 | bool _vacuum; 310 | int _mainBrushSpeed; 311 | int _sideBrushSpeed; 312 | int _vacuumSpeed; 313 | bool _debrisLED; 314 | bool _spotLED; 315 | bool _dockLED; 316 | bool _warningLED; 317 | bool _isPaused; 318 | bool _isStreaming; 319 | 320 | char _digit1; 321 | char _digit2; 322 | char _digit3; 323 | char _digit4; 324 | 325 | bool usingSoftSerial; 326 | SoftwareSerial softSerial; 327 | 328 | private: 329 | byte single_byte_packets[22] = { 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 21, 24, 34, 35, 36, 37, 38, 45, 52, 53, 58}; 330 | bool is_in_array(byte val); 331 | }; 332 | 333 | #endif --------------------------------------------------------------------------------