PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Roomba.cpp

https://github.com/clockfort/roomba
C++ | 418 lines | 218 code | 38 blank | 162 comment | 81 complexity | a1402aae1fc3cee90d779c08359e48d0 MD5 | raw file
  1. /**
  2. * ROOMBA (MiniDIN-8) Connector
  3. * INTERFACING HARDWARE INFORMATION _ _
  4. * Arduino/Roomba pinout: / |___| \
  5. * Pin 0 ---> Roomba Tx (Roomba Pin #4) / 7 6 5 \
  6. * Pin 1 ---> Roomba Rx (Roomba Pin #3) | ___ |
  7. * Pin 2 ---> Roomba Device Detect (Roomba Pin #5) | 4 |___| 3 |
  8. * | |
  9. * WARNING: Because of the use of the Arduino's natural \__2___1__/
  10. * serial rx/tx pins (pins 0 & 1) I _strongly_ encourage you
  11. * to unplug the Roomba from the Arduino while reprogramming
  12. * the Arduino.
  13. *
  14. *
  15. *
  16. *
  17. * @author Chris Lockfort
  18. **/
  19. /*
  20. * Copyright (c) 2010 Chris Lockfort
  21. *
  22. * Permission to use, copy, modify, and distribute this software for any
  23. * purpose with or without fee is hereby granted, provided that the above
  24. * copyright notice and this permission notice appear in all copies.
  25. *
  26. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  27. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  28. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  29. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  30. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  31. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  32. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33. *
  34. */
  35. #include "WProgram.h"
  36. #include "Roomba.h"
  37. /**
  38. * Default (and only!) constructor for a new Roomba object.
  39. **/
  40. Roomba::Roomba(){
  41. Serial.begin(57600);//due to high baud rate, SoftwareSerial can't be used :-(
  42. initialize();
  43. _wakePin=2;// You can change this pin if you'd like
  44. pinMode(_wakePin, OUTPUT);
  45. .
  46. _defaultDuration=64;//whole note = X/64ths of a second
  47. }
  48. /**
  49. * Wake up the roomba and put it under control of the serial interface.
  50. **/
  51. void Roomba::initialize(){
  52. /*digitalWrite(_wakePin, HIGH);
  53. delay(100);
  54. digitalWrite(_wakePin, LOW);
  55. delay(500);*/
  56. wake();
  57. digitalWrite(_wakePin, HIGH);
  58. delay(2000);
  59. Serial.print(128, BYTE); //START (Mode--> Passive)
  60. delay(50);
  61. Serial.print(130, BYTE); //CONTROL (Mode--> Safe)
  62. delay(50);
  63. }
  64. void Roomba::goForward(int velocity=200) {
  65. drive(velocity, 32768);
  66. }
  67. void Roomba::goBackward(int velocity=200) {
  68. drive( -velocity, 32768);
  69. }
  70. void Roomba::goLeft(int degrees = 90) {
  71. drive(200,1);
  72. delay(degrees*4);//just guessing for now, will add magic value later
  73. }
  74. void Roomba::goRight(int degrees = 90) {
  75. drive(200,-1);
  76. delay(degrees*4);
  77. }
  78. /**
  79. * Use the roomba's motors to move at the specified speed in the specified direction.
  80. * Please note that "dead straight" corresponds to the special radius value of 32768.
  81. * For convenience, the programmer can call this as:
  82. * <code>roomba.drive(500,STRAIGHT);</code>
  83. * To mean that the roomba should drive forward full speed.
  84. *
  85. * @param velocity The velocity, in mm/s. Range: -500mm/s to 500mm/s.
  86. * Negative is taken to mean "backwards".
  87. * @param radius The radius of curvature, in mm. Range: -2000mm to 2000mm, or 32768.
  88. * Negative is taken to mean right/clockwise, and positive means left/counter-clockwise.
  89. **/
  90. void Roomba::drive(int velocity, int radius){
  91. //I put this if(...) sanity check in here because I have honestly no idea what the roomba would do
  92. //outside of this range of values. It's not even documented :-(
  93. if(velocity>=-500 && velocity<=500 && ((radius>=-2000 && radius<=2000) || radius==32768)){
  94. Serial.print(137,BYTE); //DRIVE
  95. Serial.print((velocity>>8 & 0xFF), BYTE); //Split 16-bit velocity into 2 bytes and send
  96. Serial.print((velocity & 0xFF), BYTE);
  97. Serial.print((radius>>8 & 0xFF), BYTE);
  98. Serial.print((radius & 0xFF), BYTE);
  99. }
  100. }
  101. void Roomba::disco(){
  102. led('s');
  103. delay(150);
  104. led('c');
  105. delay(150);
  106. led('m');
  107. delay(150);
  108. led('d');
  109. delay(150);
  110. }
  111. void Roomba::led(char type){
  112. Serial.print(139, BYTE);
  113. if(type=='s')
  114. Serial.print(B00001000, BYTE);
  115. else if (type == 'c')
  116. Serial.print(B00000100, BYTE);
  117. else if (type == 'm')
  118. Serial.print(B00000010, BYTE);
  119. else if (type == 'd')
  120. Serial.print(B00000001, BYTE);
  121. Serial.print(0, BYTE);
  122. Serial.print(0xff, BYTE);
  123. }
  124. /**
  125. * Download from the Roomba all the sensor values.
  126. * However, the act of downloading this information resets some of this information on the Roomba.
  127. * For instance, 'distance' refers to the distance (in mm) traveled since the last time this sensor
  128. * data was downloaded from the Roomba.
  129. **/
  130. void Roomba::updateSensors() {
  131. /*
  132. Serial.print(142, BYTE);
  133. Serial.print(0, BYTE); // sensor packet 1, 10 bytes
  134. delay(100); // wait for sensors
  135. char i = 0;
  136. while(Serial.available()) {
  137. int c = Serial.read();
  138. if( c==-1 ) {
  139. //ERROR
  140. }
  141. sensorbytes[i++] = c;
  142. }*/
  143. }
  144. /**
  145. * Sets the baud rate of the Arduino and of the Roomba. Ignores you
  146. * if you choose an invalid baud rate.
  147. * Not so sure why you'd want to do this, except maybe to pop it up to
  148. * 115200 baud for some fine-grained control.
  149. *
  150. * WARNING: The Arduino will slip back into its original default baud rate
  151. * (57600 baud) upon loss of battery power.
  152. *
  153. * Code note: This is pretty much why switch statements are awesome.
  154. **/
  155. void Roomba::setBaud(int baudRate){
  156. char i=0;
  157. switch(baudRate){
  158. case 115200:
  159. i++;
  160. case 57600:
  161. i++;
  162. case 38400:
  163. i++;
  164. case 28800:
  165. i++;
  166. case 19200:
  167. i++;
  168. case 14400:
  169. i++;
  170. case 9600:
  171. i++;
  172. case 4800:
  173. i++;
  174. case 2400:
  175. i++;
  176. case 1200:
  177. i++;
  178. case 600:
  179. i++;
  180. case 300:
  181. Serial.print(129, BYTE); //BAUD (mode-->passive)
  182. Serial.print(i, BYTE);
  183. Serial.begin(baudRate);
  184. delay(100);//wait 100ms as per manufacturer specs
  185. default:
  186. break;
  187. }
  188. }
  189. /**
  190. * Puts the roomba into sleep mode. This is exactly equivalent
  191. * to pressing the "power" button when the device isn't being
  192. * controlled by some moron with an Arduino.
  193. *
  194. * I guess you could use this if you wanted to save power, or,
  195. * even better, if you to use the hardware serial lines for something else,
  196. * like sending debug data to the computer from the Arduino.
  197. * (i.e. roomba.sleep(); Serial.println("Debug message"); roomba.wake();)
  198. *
  199. * @see wake()
  200. */
  201. void Roomba::sleep(){
  202. digitalWrite(_wakePin, LOW); //set DD pin low just in case
  203. Serial.print(133, BYTE); //POWER
  204. }
  205. /**
  206. * Wake the roomba from sleep mode.
  207. * @see sleep()
  208. */
  209. void Roomba::wake(){
  210. digitalWrite(_wakePin, HIGH);
  211. delay(500);
  212. digitalWrite(_wakePin, LOW);
  213. }
  214. /**
  215. * Puts the roomba into spot-cleaning mode, where it spirals out
  216. * from its current location, cleaning the floor.
  217. * This command is exactly equivalent to pressing the "spot" button
  218. * when the device isn't being controlled by some moron with an Arduino.
  219. **/
  220. void Roomba::spotClean(){
  221. Serial.print(134, BYTE); //SPOT (mode-->passive)
  222. }
  223. /**
  224. * Puts the roomba into normal cleaning mode.
  225. *
  226. * This command is exactly equivalent to pressing the "clean" button
  227. * when the device isn't being controlled by some moron with an Arduino.
  228. **/
  229. void Roomba::clean(){
  230. Serial.print(135, BYTE); //CLEAN (mode-->passive)
  231. }
  232. /**
  233. * Puts the roomba into maximum time cleaning mode.
  234. * (MAX = go until battery is dead)
  235. *
  236. * This command is exactly equivalent to pressing the "clean" button
  237. * when the device isn't being controlled by some moron with an Arduino.
  238. **/
  239. void Roomba::maxClean(){
  240. Serial.print(136, BYTE); //MAX (mode-->passive)
  241. }
  242. /**
  243. * Well, the Roomba's default way of doing songs is fairly deficient from a
  244. * user-interface perspective. This way is much more user-friendly, at the
  245. * cost of some processing power/ memory. My floor (Computer Science House)
  246. * owns a vintage IBM PCJr, and this is essentially it's "play" command
  247. * from its BASIC interpreter.
  248. *
  249. * Code note: I'm fairly sure my roommate owes me a Jolt Cola for this.
  250. * I mean, PCJr? Really?
  251. */
  252. void Roomba::PCJrPlay(char song[]){
  253. int arrayLength=0;
  254. for(int i=0; song[i]!='\0'; i++){arrayLength=i+1;}
  255. char temp[32]={0};
  256. int temp_index=0;
  257. char thisNote=0;
  258. char thisOctave=3;
  259. char thisDuration=64;
  260. for(int i=0; song[i]!='\0'; i++){
  261. //if(note)
  262. if((song[i]>='A' && song[i]<='G') || (song[i]>='a' && song[i]<='g')){
  263. //Set note to it's fundamental frequency
  264. if(song[i]=='A' || song[i]=='a'){thisNote=33;}
  265. if(song[i]=='B' || song[i]=='b'){thisNote=35;}
  266. if(song[i]=='C' || song[i]=='c'){thisNote=36;}
  267. if(song[i]=='D' || song[i]=='d'){thisNote=38;}
  268. if(song[i]=='E' || song[i]=='e'){thisNote=40;}
  269. if(song[i]=='F' || song[i]=='f'){thisNote=41;}
  270. if(song[i]=='G' || song[i]=='g'){thisNote=43;}
  271. //Didn't want to use multiplication, too much memory usage
  272. switch(thisOctave){
  273. case 5:
  274. thisNote=thisNote+12;
  275. case 4:
  276. thisNote=thisNote+12;
  277. case 3:
  278. thisNote=thisNote+12;
  279. case 2:
  280. thisNote=thisNote+12;
  281. }
  282. thisNote=(thisNote-1)*12+thisNote;
  283. //WRITE OUT NOTE BYTE
  284. temp[temp_index]=thisNote;
  285. //if(whole note through eight note)
  286. if(song[i+1]>='1' && song[i+1]<='8'){ //Eventually I will use tempo here
  287. i++;
  288. if(song[i]=='1'){
  289. if(i+1<=arrayLength)
  290. if(song[++i]==6)
  291. temp[temp_index++]=4; //sixteenth note
  292. temp[temp_index++]=64; //whole note
  293. }
  294. if(song[i]=='2'){ temp[temp_index++]=32;}//half note
  295. if(song[i]=='4'){ temp[temp_index++]=16;}//quarter note
  296. if(song[i]=='8'){ temp[temp_index++]=8; } //eighth note
  297. if(song[i]=='3' && i+1<=arrayLength)
  298. if(song[++i]=='2')
  299. temp[temp_index++]=2; //32nd note
  300. if(song[i]=='6' && i+1<=arrayLength)
  301. if(song[++i]=='4')
  302. temp[temp_index++]=1; //64th note
  303. }
  304. //else, no special duration specified, use current
  305. else{
  306. temp[temp_index++]=thisDuration;
  307. }
  308. }
  309. //Octave change, Ox notation
  310. if(song[i]=='o' || song[i]=='O'){
  311. //Octave zero doesn't exist on the roomba
  312. if(song[i+1]>='1' && song[i+1]<='9'){
  313. thisOctave=song[i+1]-48;
  314. }
  315. i++;
  316. }
  317. //Octave change, arrow notation
  318. if(song[i]=='>' && thisOctave !=5)
  319. thisOctave++;
  320. if(song[i]=='<' && thisOctave!=1)
  321. thisOctave--;
  322. //Note length change, L notation
  323. if(song[i]=='L' || song[i]=='l'){
  324. if(song[i+1]>='1' && song[i+1]<='8')
  325. i++;
  326. if(song[i]=='1'){
  327. if(i+1<=arrayLength)
  328. if(song[++i]==6){
  329. thisDuration=4; //sixteenth note
  330. }
  331. thisDuration=64; //whole note
  332. }
  333. else if(song[i]=='2'){ thisDuration=32;}//half note
  334. else if(song[i]=='4'){ thisDuration=16;}//quarter note
  335. else if(song[i]=='8'){ thisDuration=8; } //eighth note
  336. else if(song[i]=='3' && i+1<=arrayLength){
  337. if(song[++i]=='2')
  338. thisDuration=2; //32nd note
  339. }
  340. else if(song[i]=='6' && i+1<=arrayLength)
  341. if(song[++i]=='4')
  342. thisDuration=1; //64th note
  343. }
  344. }//for
  345. setSong(temp, temp_index+1, 15);
  346. playSong(15);
  347. }
  348. /**
  349. * This will work much nicer if you choose nice perfect-square numbers
  350. * as a tempo.
  351. **/
  352. void Roomba::PCJrTempo(int tempo){
  353. _defaultDuration=3840/tempo; //convert BPM to # of 1/64sec periods
  354. }
  355. /**
  356. * Set the song in the roomba's memory.
  357. *
  358. * Format:
  359. * song[0]= Note number (MIDI format, aka 60=C, 61=C#)
  360. * Valid range is 31(G) to 127(G)
  361. * song[1]= Duration of note in 1/64 second periods
  362. * song[2]= Note number...
  363. * song[3]= Duration...
  364. * (etc)
  365. *
  366. * Songs can only be 16 notes long, due to internal roomba limitations.
  367. **/
  368. void Roomba::setSong(char song[], char songLength, char songNumber){
  369. if(songLength>0 && songLength<17){
  370. Serial.print(140, BYTE); //SONG
  371. Serial.print(songNumber, BYTE);
  372. Serial.print(songLength, BYTE);
  373. //apologies if the bitshift is confusing, I'm trying to avoid
  374. // libraries for multiplication,etc being drawn into the compile
  375. // and taking up extra memory space
  376. for(int i=0; song[i]<(songLength << 1); i++){Serial.print(song[i]);}
  377. }
  378. }
  379. void Roomba::playSong(char songNumber){
  380. }