Rubik's Cube
Can a wooden block, wrapped in LED strips be turned into Rubik's cube?
Components and supplies
Through Hole Resistor, 470 ohm
Arduino UNO
Alphanumeric LCD, 16 x 2
Capacitor 10 µF
Resistor 100k ohm
Pushbutton Switch, Pushbutton
Resistor 2.21k ohm
Grove - WS2813 RGB LED Strip Waterproof - 60 LED/m - 1m
Tools and machines
Scissor, Electrician
Soldering iron (generic)
Project description
Code
choices.h
c_cpp
Reads User input from homemade input panel.
1/* 2 Choices.h - Library for multiple choice via analog input 3 Created by Koen Meesters, February 21st 2019 4 Last edited by Koen Meesters, February 21st 2019 5*/ 6#ifndef choices_h 7#define choices_h 8 9#include "Arduino.h" 10 11class Choices 12{ 13 public: 14 Choices(int AIpin, int numOpt); 15 int getOption(); 16 const int arrowUp = 1; 17 const int arrowLeft = 2; 18 const int arrowCentre = 3; 19 const int arrowRight = 4; 20 const int arrowDown = 5; 21 const int highBut = 1; 22 const int midBut = 2; 23 const int lowBut = 3; 24 25 private: 26 int _AIpin; 27 int _numOpt; 28 int readValue(); 29}; 30 31#endif 32
Rubik's Cube
c_cpp
Main program
1/* this program turns a ledstrip glued to a wooden cube into a Rubiks Cube 2 * 3 */ 4//major changes on March 18th 2019 5#include "choices.h" 6#include "cube.h" 7#include "output.h" 8#include <LiquidCrystal.h> 9#include <PololuLedStrip.h> 10 11Choices LeftPanel(A5, 5); //set 5 options at pin A5 12Choices RightPanel(A4, 3);//set 3 options at pin A3 13Cube MyCube(3); //would be nice to have DO port of ledstrip as an argument 14Output MyOutput(false, true); //output to Serial output and or lcd output 15 16#define _dutch //dutch language selected, conditional compiling is applied because insufficient dynamic memory to store 2 languages 17 18void setup() { 19 Serial.begin(9600); 20 delay(500); 21 MyCube.show(); 22 delay(500); 23 MyOutput.clrscr(); 24#ifndef _dutch 25 MyOutput.txt("Hello there!"); //default language 26#else 27 MyOutput.txt("Hallo daar!"); 28#endif 29 MyCube.littleShow(); 30 MyOutput.clrscr(); 31} 32 33 34 35const int turnSide = 0; 36const int turnMid = 1; 37const int turnCube = 2; 38const int undoLast = 3; 39const int solveCube = 4; 40 41const bool CW = true; 42 43const int histMax = 30; //max number of turns in memory 44int turnNr = 0; //number of turns done 45int lastSolved = 0; //last time that cube was solved 46int turnsInHist = 0; //current number of turns in history 47byte turnHist[histMax]; 48/* turns are stored in turnHist 49 * 1 byte is used to store turn 50 * 1 byte may represent numbers from 0 - 255 51 * coding: 100*dir + 10 * side + task 52 * history should be implemented in Class Cube 53 */ 54bool escFlag = false; 55 56void loop() { 57 58 int task; 59 int side; 60 bool dir; 61 62 escFlag = false; 63 task = getTask(); 64 switch (task) { 65 case turnSide: 66 while (true) { 67 side = getSide(); 68 if (escFlag) break; 69 dir = getDir(); 70 if (escFlag) break; 71 MyCube.showTurnSide(side, dir); 72 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 73 turnNr++; 74 turnsInHist++; 75 } 76 break; 77 case turnMid: 78 while (true) { 79 side = getSide(); 80 if (escFlag) break; 81 dir = getDir(); 82 if (escFlag) break; 83 MyCube.showTurnMid(side, dir); 84 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 85 turnNr++; 86 turnsInHist++; 87 } 88 break; 89 case turnCube: 90 while (true) { 91 side = getSide(); 92 if (escFlag) break; 93 dir = getDir(); 94 if (escFlag) break; 95 MyCube.showTurnCube(side,dir); 96 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 97 turnNr++; 98 turnsInHist++; 99 } 100 break; 101 case undoLast: 102 do { 103 if (turnsInHist == 0) { 104 MyOutput.clrscr(); 105#ifndef _dutch 106 MyOutput.txt("Cannot undo"); 107#else 108 MyOutput.txt("Ongedaan maken"); 109 MyOutput.nxtln(); 110 MyOutput.txt("niet mogelijk"); 111#endif 112 delay(1000); 113 escFlag = true; 114 } 115 if (escFlag) break; 116 turnNr--; 117 turnsInHist--; 118 undoLastTurn(); 119 delay(500); 120 if (turnsInHist == 0) break; 121 MyOutput.clrscr(); 122#ifndef _dutch 123 MyOutput.txt("Undo another"); 124 MyOutput.nxtln(); 125 MyOutput.txt("turn?"); 126#else 127 MyOutput.txt("Nogmaals"); 128 MyOutput.nxtln(); 129 MyOutput.txt("terug draaien?"); 130#endif 131 while (!escFlag) { 132 int choice = RightPanel.getOption(); 133 if (choice == RightPanel.highBut) { 134 escFlag = true; 135 break; 136 } 137 if (choice == RightPanel.lowBut) { 138 break; 139 } 140 } 141 } while (!escFlag); 142 break; 143 case solveCube: 144 if ((turnsInHist == 0) || ((turnNr-lastSolved)>turnsInHist)) { //not enough memory to undo until solved 145 MyCube.reInit(); 146 lastSolved = 0; 147 turnNr = 0; 148 turnsInHist = 0; 149 } 150 else { 151 while(turnNr > lastSolved) { 152 turnNr--; 153 turnsInHist--; 154 undoLastTurn(); 155 delay(500); 156 } 157 } 158 break; 159 } 160 if (MyCube.checkCubeSolved()) { 161 MyOutput.clrscr(); 162#ifndef _dutch 163 MyOutput.txt("Cube solved!"); 164#else 165 MyOutput.txt("Kubus opgelost!"); 166#endif 167 for (int i=0; i<5; i++) { 168 delay(500); 169 MyCube.dark(); 170 delay(500); 171 MyCube.show(); 172 lastSolved = turnNr; 173 } 174 } 175 if (turnsInHist>histMax) turnsInHist = histMax; 176 delay(500); 177} 178 179void undoLastTurn() { 180 byte dir = turnHist[turnNr%histMax]/100; 181 byte side = (turnHist[turnNr%histMax]-100*dir)/10; 182 byte task = turnHist[turnNr%histMax]-100*dir-10*side; 183 if (task == turnSide) { 184 MyCube.showTurnSide(side,!dir); 185 } 186 if (task == turnMid) { 187 MyCube.showTurnMid(side,!dir); 188 } 189 if (task == turnCube) { 190 MyCube.showTurnCube(side,!dir); 191 } 192} 193 194 195int getTask() { 196#ifndef _dutch 197 const String Tasks[] = {"Turn side ", 198 "Turn mids ", 199 "Turn cube ", 200 "Undo turn ", 201 "Solve cube"}; 202#else 203 const String Tasks[] = {"Draai zijkant", 204 "Draai midden ", 205 "Draai kubus ", 206 "Draai terug ", 207 "Los kubus op "}; 208#endif 209 210 MyOutput.clrscr(); 211#ifndef _dutch 212 MyOutput.txt("Choose task>"); 213#else 214 MyOutput.txt("Kies taak>"); 215#endif 216 MyOutput.nxtln(); 217 int ans = turnSide; 218 MyOutput.txt(Tasks[ans]); 219 MyOutput.nxtln(); 220 while (!confirmed()) { 221 int old_ans = ans; 222 int userInput = LeftPanel.getOption(); 223 if (userInput == LeftPanel.arrowUp || userInput == LeftPanel.arrowRight) ans++; 224 if (userInput == LeftPanel.arrowDown || userInput == LeftPanel.arrowLeft) ans--; 225 if (ans==-1) ans = solveCube; 226 if (ans== 5) ans = turnSide; 227 if (ans != old_ans) { 228 MyOutput.txt(Tasks[ans]); 229 MyOutput.nxtln(); 230 delay(100); 231 } 232 } 233 MyOutput.nxtln(); 234 MyOutput.clrscr(); 235#ifndef _dutch 236 MyOutput.txt("Choice= "); 237#else 238 MyOutput.txt("Keuze= "); 239#endif 240 MyOutput.nxtln(); 241 MyOutput.txt(Tasks[ans]); 242 delay(1000); 243 return ans; 244} 245 246 247int getSide() { 248 249#ifndef _dutch 250 const String Txts[] = {"Turn ", 251 " side"}; 252 const String SidesTxt[] = {"ground", 253 "front ", 254 " top ", 255 " back ", 256 " left ", 257 "right "}; 258 259#else 260 const String Txts[] = {"Draai ", 261 "kant"}; 262 263 const String SidesTxt[] = {" onder", 264 " voor", 265 " boven", 266 "achter", 267 "linker", 268 "rechter"}; 269#endif 270 271 MyOutput.nxtln(); 272 MyOutput.clrscr(); 273#ifndef _dutch 274 MyOutput.txt("Choose side/axis>"); 275#else 276 MyOutput.txt("Kies zijkant/as>"); 277#endif 278 MyOutput.nxtln(); 279 MyOutput.txt(Txts[0]); 280 int ans = MyCube.frontSide; 281 MyOutput.txt(SidesTxt[ans]); 282 MyOutput.txt(Txts[1]); 283 284 while (!confirmed()) { 285 if (RightPanel.getOption() == RightPanel.highBut) { 286 escFlag = true; 287 return; 288 } 289 int old_ans = ans; 290 int userInput = LeftPanel.getOption(); 291 if (ans > -1 && ans < 4) { 292 if (userInput == LeftPanel.arrowUp) { 293 ans++; 294 if (ans == 4) { 295 ans = MyCube.groundSide; 296 } 297 } 298 if (userInput == LeftPanel.arrowDown) { 299 ans--; 300 if (ans == -1) { 301 ans = MyCube.backSide; 302 } 303 } 304 if (userInput == LeftPanel.arrowLeft) { 305 ans = MyCube.leftSide; 306 } 307 if (userInput == LeftPanel.arrowRight) { 308 ans = MyCube.rightSide; 309 } 310 } 311 if (ans == MyCube.leftSide) { 312 if (userInput == LeftPanel.arrowRight) { 313 ans = MyCube.frontSide; 314 } 315 if (userInput == LeftPanel.arrowUp) { 316 ans = MyCube.topSide; 317 } 318 if (userInput == LeftPanel.arrowDown) { 319 ans = MyCube.groundSide; 320 } 321 } 322 if (ans == MyCube.rightSide) { 323 if (userInput == LeftPanel.arrowLeft) { 324 ans = MyCube.frontSide; 325 } 326 if (userInput == LeftPanel.arrowUp) { 327 ans = MyCube.topSide; 328 } 329 if (userInput == LeftPanel.arrowDown) { 330 ans = MyCube.groundSide; 331 } 332 } 333 if (userInput == LeftPanel.arrowCentre) { 334 ans = MyCube.frontSide; 335 } 336 if (ans != old_ans) { 337 MyOutput.nxtln(); 338 MyOutput.txt(Txts[0]); 339 MyOutput.txt(SidesTxt[ans]); 340 MyOutput.txt(Txts[1]); 341 delay(100); 342 } 343 } 344 MyOutput.clrscr(); 345#ifndef _dutch 346 MyOutput.txt("Choice= "); 347#else 348 MyOutput.txt("Keuze = "); 349#endif 350 MyOutput.nxtln(); 351 MyOutput.txt(Txts[0]); 352 MyOutput.txt(SidesTxt[ans]); 353 MyOutput.txt(Txts[1]); 354 delay(1000); 355 return ans; 356} 357 358 359bool getDir() { 360 361#ifndef _dutch 362 const String DirTxt[] = {"Left turn", 363 "Right turn"}; 364#else 365 const String DirTxt[] = {"Linksom ", 366 "Rechtsom"}; 367#endif 368 bool CW = 1; 369 bool ans = CW; 370 371 MyOutput.clrscr(); 372#ifndef _dutch 373 MyOutput.txt("Choose direction>"); 374#else 375 MyOutput.txt("Kies richting>"); 376#endif 377 MyOutput.nxtln(); 378 MyOutput.txt(DirTxt[ans]); 379 while (!confirmed()) { 380 if (RightPanel.getOption() == RightPanel.highBut) { 381 escFlag = true; 382 return; 383 } 384 bool old_ans = ans; 385 int userInput = LeftPanel.getOption(); 386 if (userInput != 0) { 387 ans = !ans; 388 } 389 if (ans != old_ans) { 390 MyOutput.nxtln(); 391 MyOutput.txt(DirTxt[ans]); 392 delay(100); 393 } 394 } 395 MyOutput.clrscr(); 396#ifndef _dutch 397 MyOutput.txt("Direction="); 398#else 399 MyOutput.txt("Richting="); 400#endif 401 MyOutput.nxtln(); 402 MyOutput.txt(DirTxt[ans]); 403 delay(1000); 404 return ans; 405} 406 407 408bool confirmed() { 409 int UserInput = RightPanel.getOption(); 410 if (UserInput == RightPanel.lowBut) { 411 return 1; 412 } 413 return 0; 414} 415
output.h
c_cpp
Writes output to LCD and serial in one go (mainly handy for trouble shooting)
1/* 2 output.h - Library to handle serial and lcd screen output 3 Created by Koen Meesters, March 2019 4 Last edited by Koen Meesters, April 13th 2019 5*/ 6#ifndef output_h 7#define output_h 8 9#include "Arduino.h" 10#include <LiquidCrystal.h> 11 12class Output 13{ 14 public: 15 Output(bool serial,bool cryst); 16 int noIdeaWhy; 17 void txt(String); 18 void nxtln(); 19 void clrscr(); 20 private: 21 bool _serial; 22 bool _cryst; 23}; 24 25#endif 26
cube.cpp
c_cpp
Cube: Library that represents and manipulates Cube
1/* 2 Cube.h - Library for Rubik's Cube 3 Created by Koen Meesters, March 5th 2019 4 Last edited by Koen Meesters, 5*/ 6#define LED_COUNT 54 7 8#include "Arduino.h" 9#include "cube.h" 10#include <PololuLedStrip.h> 11 12 13PololuLedStrip<2> ledStrip; //Sturing ledstrip op DO 2 14 15Cube::Cube(int) { 16 17 int _noIdeaWhy=noIdeaWhy; 18 19 //initialize cube 20 for (int curSide = 0; curSide < numSides; curSide++) { //fill all sides 21 for (int curPos = 0; curPos < numPos; curPos++) { //fill all positions 22 Color[curSide][curPos] = 10*curSide + curPos; 23 } 24 } 25 26 //initialize Rims 01 02 03 04 05 06 07 08 09 10 11 12 27 byte rims[6][12][2] = {{{1,7},{1,6},{1,5},{5,7},{5,6},{5,5},{3,3},{3,2},{3,1},{4,7},{4,6},{4,5}}, //rims side 0 28 {{2,7},{2,6},{2,5},{5,1},{5,8},{5,7},{0,3},{0,2},{0,1},{4,5},{4,4},{4,3}}, //rims side 1 29 {{3,7},{3,6},{3,5},{5,3},{5,2},{5,1},{1,3},{1,2},{1,1},{4,3},{4,2},{4,1}}, //rims side 2 30 {{0,7},{0,6},{0,5},{5,5},{5,4},{5,3},{2,3},{2,2},{2,1},{4,1},{4,8},{4,7}}, //rims side 3 31 32 {{2,1},{2,8},{2,7},{1,1},{1,8},{1,7},{0,1},{0,8},{0,7},{3,1},{3,8},{3,7}}, //rims side 4 33 {{2,5},{2,4},{2,3},{3,5},{3,4},{3,3},{0,5},{0,4},{0,3},{1,5},{1,4},{1,3}}};//rims side 5 34 35 for (int curSide = 0; curSide < numSides; curSide++) { 36 for (int curRimPos = 0; curRimPos < numRimPos; curRimPos++) { 37 const int side=0; //element index of Side in rims 38 const int pos=1; //element index of Position in rims 39 rimsPtr[curSide][curRimPos] = &Color[rims[curSide][curRimPos][side]][rims[curSide][curRimPos][pos]]; 40 } 41 } 42 43 //intitialize Mids 01 02 03 04 05 06 07 08 09 10 11 12 44 byte mids[6][12][2] = {{{1,8},{1,0},{1,4},{5,8},{5,0},{5,4},{3,4},{3,0},{3,8},{4,8},{4,0},{4,4}}, //mids side 0 45 {{2,8},{2,0},{2,4},{5,2},{5,0},{5,6},{0,4},{0,0},{0,8},{4,6},{4,0},{4,2}}, //mids side 1 46 {{3,8},{3,0},{3,4},{5,4},{5,0},{5,8},{1,4},{1,0},{1,8},{4,4},{4,0},{4,8}}, //mids side 2 47 {{0,8},{0,0},{0,4},{5,6},{5,0},{5,2},{2,4},{2,0},{2,8},{4,2},{4,0},{4,6}}, //mids side 3 48 49 {{2,2},{2,0},{2,6},{1,2},{1,0},{1,6},{0,2},{0,0},{0,6},{3,2},{3,0},{3,6}}, //mids side 4 50 {{2,6},{2,0},{2,2},{3,6},{3,0},{3,2},{0,6},{0,0},{0,2},{1,6},{1,0},{1,2}}};//mids side 5 51 52 for (int curSide = 0; curSide < numSides; curSide++) { 53 for (int curMidPos = 0; curMidPos < numMidPos; curMidPos++) { 54 const int side=0; //element index of Side in mids 55 const int pos=1; //element index of Position in mids 56 midsPtr[curSide][curMidPos] = &Color[mids[curSide][curMidPos][side]][mids[curSide][curMidPos][pos]]; 57 } 58 } 59} 60/* 61 Each side has 9 positions 62 [ 1][ 2][ 3] 63 [ 8][ 0][ 4] 64 [ 7][ 6][ 5] 65 66 6 sides together form a cube Ledstripn addresses 67 0 ground 68 [20] [32] [44] 69 [19] [31] [43] 70 [18] [30] [42] 71 3 Back 3 back 72 [ 1][ 2][ 3] [17] [29] [41] 73 [ 8][ 0][ 4] [16] [28] [40] 74 [ 7][ 6][ 5] [15] [27] [39] 75 2 Top 2 Top 76 [ 1][ 2][ 3] [14] [26] [38] 77 [ 8][ 0][ 4] [13] [25] [37] 78 [ 7][ 6][ 5] [12] [24] [36] 79 4 Left 1 Front 5 Right 4 Left 1 Front 5 Right 80 [ 1][ 2][ 3] [ 1][ 2][ 3] [ 1][ 2][ 3] [ 2] [ 3] [ 8] [11] [23] [35] [53] [48] [47] 81 [ 8][ 0][ 4] [ 8][ 0][ 4] [ 8][ 0][ 4] [ 1] [ 4] [ 7] [10] [22] [34] [52] [49] [46] 82 [ 7][ 6][ 5] [ 7][ 6][ 5] [ 7][ 6][ 5] [ 0] [ 5] [ 6] [ 9] [21] [33] [51] [50] [45] 83 0 Ground 84 [ 1][ 2][ 3] 85 [ 8][ 0][ 4] 86 [ 7][ 6][ 5] 87 88 89 //vertaaltqbel cube naar Ledstrip 90 31,20,32,44,43,42,30,18,19, 91 22,11,23,35,34,33,21, 9,10, 92 25,14,26,38,37,36,24,12,13, 93 28,17,29,41,40,39,27,15,16, 94 4, 2, 3, 8, 7, 6, 5, 0, 1, 95 49,53,48,47,46,45,50,51,52 96 97 98 Each side has a number and a character ID 99 0 = groundSide 100 1 = frontSide 101 2 = topSide 102 3 = backSide 103 4 = leftSide 104 5 = rightSide 105 106 The cube will have 6 * 9 = 54 RGB LEDS, so 162 LED's! 107*/ 108 109//This function checks if the cube is solved (each side has one color) 110bool Cube::checkCubeSolved() { 111 for (int curSide = 0; curSide < numSides; curSide++) { //check all sides 112 byte sideColor = Color[curSide][0]/10; 113 for (int curPos = 1; curPos < numPos; curPos++) { //fill all positions 114 if (!(Color[curSide][curPos]/10==sideColor)) return false; 115 } 116 } 117 return true; 118} 119 120//This procedure reinitializes the cube 121void Cube::reInit() { 122 for (int curSide = 0; curSide < numSides; curSide++) { //fill all sides 123 for (int curPos = 0; curPos < numPos; curPos++) { //fill all positions 124 Color[curSide][curPos] = 10*curSide + curPos; //later colours will be set here 125 } 126 } 127 show(); 128} 129 130//This procedure does a little show 131void Cube::littleShow() { 132 delay(1000); 133 for (int i=0; i<4; i++) { 134 showTurnMid(i,!CW); 135 delay(1000); 136 } 137 for (int i=3; i>=0; i--) { 138 showTurnMid(i,CW); 139 delay(1000); 140 } 141 delay(1000); 142 reInit(); 143} 144 145//This procedure shows a turn of the cube as a whole 146void Cube::showTurnCube(int side,bool dir) { 147 showTurnSide(side,dir); 148 showTurnMid(side,dir); 149 int oppSide = 0; 150 if (side<2) oppSide = side+2; 151 if (side>1 && side<4) oppSide = side-2; 152 if (side==4) oppSide = 5; 153 if (side==5) oppSide = 4; 154 showTurnSide(oppSide,!dir); 155 delay(delta_t); 156} 157 158//This procedure shows a turn of a side 159void Cube::showTurnSide(int side,bool dir) { 160 turnRim(side,dir); 161 show(); 162 delay(delta_t); 163 for (int i = 0; i<2; i++) { 164 turnSide(side,dir); 165 show(); 166 delay(delta_t); 167 turnRim(side,dir); 168 show(); 169 delay(delta_t); 170 } 171} 172 173//This procedrue shows a turn of the mids 174void Cube::showTurnMid(int side,bool dir) { 175 for (int i = 0; i<3; i++) { 176 turnMid(side,dir); 177 show(); 178 delay(delta_t); 179 } 180} 181 182//This procedure turns the side 183void Cube::turnSide(int side, bool dir) { //this procedure must be run 2 times per turn 184 byte tempStoreColor; 185 if (dir == CW) { //turn ClockWise 186 tempStoreColor = Color[side][numPos-1]; //Color value of Position 8 is temporarily stored 187 for (int curPos = numPos-2; curPos > 0; curPos--) { 188 Color[side][curPos+1] = Color[side][curPos]; //shift Color value Clockwise 189 } 190 Color[side][1] = tempStoreColor; //put stored Color value in Position 1 191 } 192 else { //turn Counter ClockWise 193 tempStoreColor = Color[side][1]; //Color value of Position 1 is temporarily stored 194 for (int curPos = 1; curPos < numPos-1; curPos++) { 195 Color[side][curPos] = Color[side][curPos+1]; //shift Color value counter Clockwise 196 } 197 Color[side][numPos-1] = tempStoreColor; //put stored Color value in Position 8 198 } 199} 200 201//this procedure turns de mids 202void Cube::turnMid(int side, bool dir) { //this procedure must be run 3 times per turn 203 byte tempStoreColor; 204 if (dir == CW) { //if turn ClockWise 205 tempStoreColor = *midsPtr[side][numRimPos-1]; //Color value of Position 12 is temporarily stored 206 for (int curPos = numMidPos-1; curPos > 0; curPos--) { 207 *midsPtr[side][curPos] = *midsPtr[side][curPos-1]; //shift Color value Clockwise 208 } 209 *midsPtr[side][0] = tempStoreColor; //put stored Color value in Position 0 210 } 211 else { //turn Counter ClockWise 212 tempStoreColor = *midsPtr[side][0]; //Color value of Position 0 is temporarily stored 213 for (int curPos = 0; curPos < numMidPos-1; curPos++) { 214 *midsPtr[side][curPos] = *midsPtr[side][curPos+1]; //shift Color value Clockwise 215 } 216 *midsPtr[side][numRimPos-1] = tempStoreColor; //put stored Color value in Position 12 217 } 218} 219 220//This procedure turns the rims of a Side 221void Cube::turnRim(int side, bool dir) { //this procedure must be run 3 times per turn 222 byte tempStoreColor; 223 if (dir == CW) { //turn ClockWise 224 tempStoreColor = *rimsPtr[side][numRimPos-1]; //Color value of Position 12 is temporarily stored 225 for (int curPos = numRimPos-1; curPos > 0; curPos--) { 226 *rimsPtr[side][curPos] = *rimsPtr[side][curPos-1]; //shift Color value Clockwise 227 } 228 *rimsPtr[side][0] = tempStoreColor; //put stored Color value in Position 0 229 } 230 else { //turn counter clockwise 231 tempStoreColor = *rimsPtr[side][0]; //Color value of Position 0 is temporarily stored 232 for (int curPos = 0; curPos < numRimPos-1; curPos++) { 233 *rimsPtr[side][curPos] = *rimsPtr[side][curPos+1]; //shift Color value counter clockwise 234 } 235 *rimsPtr[side][numRimPos-1] = tempStoreColor; //put stored Color value in Position 12 236 } 237} 238 239//This function hands the address of the cube to the ledstrip writing procedure 240const byte* Cube::handCube() { 241 return &Color[0][0]; 242} 243 244//#define _debugging 245#ifdef _debugging 246//This procedure writes the Cube colors to Serial (for debugging purposes) 247void Cube::toScreen() { 248 Serial.println("Cube"); 249 int transPos[] = { 1,2,3, 250 8,0,4, 251 7,6,5 }; 252 int curSide; 253 254 //backSide and TopSide 255 for (curSide = backSide; curSide >= topSide; curSide--) { 256 for (int i=0; i<3; i++) { // voor alle posities 257 Serial.print(" "); 258 for (int j=(3*i); j<(3+3*i); j++) { 259 int pos = transPos[j]; 260 Serial.print('['); 261 if(Color[curSide][pos]<9) {Serial.print(' ');} 262 Serial.print(Color[curSide][pos]); 263 Serial.print("] "); 264 } 265 Serial.println(); 266 } 267 Serial.println(); 268 } 269 270 //leftSide, frontSide and rightSide 271 for (int i=0; i<3; i++) { // voor alle posities 272 curSide = leftSide; 273 for (int j=(3*i); j<(3+3*i); j++) { 274 int pos = transPos[j]; 275 Serial.print('['); 276 if(Color[curSide][pos]<9) {Serial.print(' ');} 277 Serial.print(Color[curSide][pos]); 278 Serial.print("] "); 279 } 280 Serial.print(' '); 281 curSide = frontSide; 282 for (int j=(3*i); j<(3+3*i); j++) { 283 int pos = transPos[j]; 284 Serial.print('['); 285 if(Color[curSide][pos]<9) {Serial.print(' ');} 286 Serial.print(Color[curSide][pos]); 287 Serial.print("] "); 288 } 289 Serial.print(' '); 290 curSide = rightSide; 291 for (int j=(3*i); j<(3+3*i); j++) { 292 int pos = transPos[j]; 293 Serial.print('['); 294 if(Color[curSide][pos]<9) {Serial.print(' ');} 295 Serial.print(Color[curSide][pos]); 296 Serial.print("] "); 297 } 298 Serial.println(); 299 } 300 Serial.println(); 301 302 //groundSide 303 curSide = groundSide; 304 for (int i=0; i<3; i++) { // voor alle posities 305 Serial.print(" "); 306 for (int j=(3*i); j<(3+3*i); j++) { 307 int pos = transPos[j]; 308 Serial.print('['); 309 if(Color[curSide][pos]<9) {Serial.print(' ');} 310 Serial.print(Color[curSide][pos]); 311 Serial.print("] "); 312 } 313 Serial.println(); 314 } 315 Serial.println(); 316} 317#endif 318 319/* alternative Cube config 320 * 321 * 51 52 53 322 * 58 50 54 323 * 57 56 55 324 * 325 * 31 32 33 41 42 43 326 * 38 30 34 48 40 44 327 * 37 36 35 47 46 45 328 * 329 * 11 12 13 21 22 23 330 * 18 10 14 28 20 24 331 * 17 16 15 27 26 25 332 * 333 * 01 02 03 334 * 08 00 04 335 * 07 06 05 336 * 337 *rims 338 *0: 17 16 15 27 26 25 339 *1: 31 38 37 21 28 27 03 02 01 340 *2: 37 36 35 47 46 45 05 04 03 15 14 13 341 *3: 51 58 57 41 48 47 23 22 21 13 12 11 342 *4: 57 56 55 25 24 23 35 34 33 343 *5: 43 42 41 33 32 31 344 * 345 *mids 346 *0: 18 10 14 28 20 24 347 *1: 32 30 36 22 20 26 04 00 08 348 *2: 38 30 34 48 40 44 06 00 02 16 10 12 349 *3: 52 50 56 42 40 46 24 20 28 14 10 18 350 *4: 58 50 54 26 20 22 36 30 32 351 *5: 44 40 48 34 30 38 352 * 353 * 354 */ 355 356 void Cube::dark() { 357 rgb_color Dark; 358 Dark.red = 0; 359 Dark.green = 0; 360 Dark.blue = 0; 361 362 rgb_color colors[54]; 363 for (int i=0; i<54; i++) { 364 colors[i] = Dark; 365 } 366 ledStrip.write(colors, LED_COUNT); 367 } 368 369void Cube::show() { 370 371 rgb_color Red; 372 Red.red = 10; 373 Red.green = 0; 374 Red.blue = 0; 375 376 rgb_color Blue; 377 Blue.red = 0; 378 Blue.green = 0; 379 Blue.blue = 10; 380 381 rgb_color Green; 382 Green.red = 0; 383 Green.green = 10; 384 Green.blue = 0; 385 386 rgb_color Yellow; 387 Yellow.red = 60; 388 Yellow.green = 45; 389 Yellow.blue = 0; 390 391 rgb_color White; 392 White.red = 20; 393 White.green = 20; 394 White.blue = 20; 395 396 rgb_color Orange; 397 Orange.red = 45; 398 Orange.green = 15; 399 Orange.blue = 0; 400 401 rgb_color Dark; 402 Dark.red = 0; 403 Dark.green = 0; 404 Dark.blue = 0; 405 406 rgb_color Colors[54]; 407 408 const byte ledStripAddr []= {31,20,32,44,43,42,30,18,19, 409 22,11,23,35,34,33,21, 9,10, 410 25,14,26,38,37,36,24,12,13, 411 28,17,29,41,40,39,27,15,16, 412 4, 2, 3, 8, 7, 6, 5, 0, 1, 413 49,53,48,47,46,45,50,51,52}; 414 415 for (int i=0; i<54; i++) { 416 Colors[i] = Dark; 417 } 418 419 const byte* cubePtr = handCube(); 420 for (int i = 0; i<54; i++) { 421 422 byte curColor = *cubePtr/10; 423 byte curLedStripAddr = ledStripAddr[i]; 424 switch (curColor) { 425 case 0: 426 Colors[curLedStripAddr] = Yellow; 427 break; 428 case 1: 429 Colors[curLedStripAddr] = Blue; 430 break; 431 case 2: 432 Colors[curLedStripAddr] = White; 433 break; 434 case 3: 435 Colors[curLedStripAddr] = Green; 436 break; 437 case 4: 438 Colors[curLedStripAddr] = Red; 439 break; 440 case 5: 441 Colors[curLedStripAddr] = Orange; 442 break; 443 } 444 cubePtr++; 445 } 446 ledStrip.write(Colors, LED_COUNT); 447} 448 449 450
Rubik's Cube
c_cpp
Main program
1/* this program turns a ledstrip glued to a wooden cube into a Rubiks Cube 2 * 3 */ 4//major changes on March 18th 2019 5#include "choices.h" 6#include "cube.h" 7#include "output.h" 8#include <LiquidCrystal.h> 9#include <PololuLedStrip.h> 10 11Choices LeftPanel(A5, 5); //set 5 options at pin A5 12Choices RightPanel(A4, 3);//set 3 options at pin A3 13Cube MyCube(3); //would be nice to have DO port of ledstrip as an argument 14Output MyOutput(false, true); //output to Serial output and or lcd output 15 16#define _dutch //dutch language selected, conditional compiling is applied because insufficient dynamic memory to store 2 languages 17 18void setup() { 19 Serial.begin(9600); 20 delay(500); 21 MyCube.show(); 22 delay(500); 23 MyOutput.clrscr(); 24#ifndef _dutch 25 MyOutput.txt("Hello there!"); //default language 26#else 27 MyOutput.txt("Hallo daar!"); 28#endif 29 MyCube.littleShow(); 30 MyOutput.clrscr(); 31} 32 33 34 35const int turnSide = 0; 36const int turnMid = 1; 37const int turnCube = 2; 38const int undoLast = 3; 39const int solveCube = 4; 40 41const bool CW = true; 42 43const int histMax = 30; //max number of turns in memory 44int turnNr = 0; //number of turns done 45int lastSolved = 0; //last time that cube was solved 46int turnsInHist = 0; //current number of turns in history 47byte turnHist[histMax]; 48/* turns are stored in turnHist 49 * 1 byte is used to store turn 50 * 1 byte may represent numbers from 0 - 255 51 * coding: 100*dir + 10 * side + task 52 * history should be implemented in Class Cube 53 */ 54bool escFlag = false; 55 56void loop() { 57 58 int task; 59 int side; 60 bool dir; 61 62 escFlag = false; 63 task = getTask(); 64 switch (task) { 65 case turnSide: 66 while (true) { 67 side = getSide(); 68 if (escFlag) break; 69 dir = getDir(); 70 if (escFlag) break; 71 MyCube.showTurnSide(side, dir); 72 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 73 turnNr++; 74 turnsInHist++; 75 } 76 break; 77 case turnMid: 78 while (true) { 79 side = getSide(); 80 if (escFlag) break; 81 dir = getDir(); 82 if (escFlag) break; 83 MyCube.showTurnMid(side, dir); 84 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 85 turnNr++; 86 turnsInHist++; 87 } 88 break; 89 case turnCube: 90 while (true) { 91 side = getSide(); 92 if (escFlag) break; 93 dir = getDir(); 94 if (escFlag) break; 95 MyCube.showTurnCube(side,dir); 96 turnHist[turnNr%histMax]= 100*dir+ 10*side + task; 97 turnNr++; 98 turnsInHist++; 99 } 100 break; 101 case undoLast: 102 do { 103 if (turnsInHist == 0) { 104 MyOutput.clrscr(); 105#ifndef _dutch 106 MyOutput.txt("Cannot undo"); 107#else 108 MyOutput.txt("Ongedaan maken"); 109 MyOutput.nxtln(); 110 MyOutput.txt("niet mogelijk"); 111#endif 112 delay(1000); 113 escFlag = true; 114 } 115 if (escFlag) break; 116 turnNr--; 117 turnsInHist--; 118 undoLastTurn(); 119 delay(500); 120 if (turnsInHist == 0) break; 121 MyOutput.clrscr(); 122#ifndef _dutch 123 MyOutput.txt("Undo another"); 124 MyOutput.nxtln(); 125 MyOutput.txt("turn?"); 126#else 127 MyOutput.txt("Nogmaals"); 128 MyOutput.nxtln(); 129 MyOutput.txt("terug draaien?"); 130#endif 131 while (!escFlag) { 132 int choice = RightPanel.getOption(); 133 if (choice == RightPanel.highBut) { 134 escFlag = true; 135 break; 136 } 137 if (choice == RightPanel.lowBut) { 138 break; 139 } 140 } 141 } while (!escFlag); 142 break; 143 case solveCube: 144 if ((turnsInHist == 0) || ((turnNr-lastSolved)>turnsInHist)) { //not enough memory to undo until solved 145 MyCube.reInit(); 146 lastSolved = 0; 147 turnNr = 0; 148 turnsInHist = 0; 149 } 150 else { 151 while(turnNr > lastSolved) { 152 turnNr--; 153 turnsInHist--; 154 undoLastTurn(); 155 delay(500); 156 } 157 } 158 break; 159 } 160 if (MyCube.checkCubeSolved()) { 161 MyOutput.clrscr(); 162#ifndef _dutch 163 MyOutput.txt("Cube solved!"); 164#else 165 MyOutput.txt("Kubus opgelost!"); 166#endif 167 for (int i=0; i<5; i++) { 168 delay(500); 169 MyCube.dark(); 170 delay(500); 171 MyCube.show(); 172 lastSolved = turnNr; 173 } 174 } 175 if (turnsInHist>histMax) turnsInHist = histMax; 176 delay(500); 177} 178 179void undoLastTurn() { 180 byte dir = turnHist[turnNr%histMax]/100; 181 byte side = (turnHist[turnNr%histMax]-100*dir)/10; 182 byte task = turnHist[turnNr%histMax]-100*dir-10*side; 183 if (task == turnSide) { 184 MyCube.showTurnSide(side,!dir); 185 } 186 if (task == turnMid) { 187 MyCube.showTurnMid(side,!dir); 188 } 189 if (task == turnCube) { 190 MyCube.showTurnCube(side,!dir); 191 } 192} 193 194 195int getTask() { 196#ifndef _dutch 197 const String Tasks[] = {"Turn side ", 198 "Turn mids ", 199 "Turn cube ", 200 "Undo turn ", 201 "Solve cube"}; 202#else 203 const String Tasks[] = {"Draai zijkant", 204 "Draai midden ", 205 "Draai kubus ", 206 "Draai terug ", 207 "Los kubus op "}; 208#endif 209 210 MyOutput.clrscr(); 211#ifndef _dutch 212 MyOutput.txt("Choose task>"); 213#else 214 MyOutput.txt("Kies taak>"); 215#endif 216 MyOutput.nxtln(); 217 int ans = turnSide; 218 MyOutput.txt(Tasks[ans]); 219 MyOutput.nxtln(); 220 while (!confirmed()) { 221 int old_ans = ans; 222 int userInput = LeftPanel.getOption(); 223 if (userInput == LeftPanel.arrowUp || userInput == LeftPanel.arrowRight) ans++; 224 if (userInput == LeftPanel.arrowDown || userInput == LeftPanel.arrowLeft) ans--; 225 if (ans==-1) ans = solveCube; 226 if (ans== 5) ans = turnSide; 227 if (ans != old_ans) { 228 MyOutput.txt(Tasks[ans]); 229 MyOutput.nxtln(); 230 delay(100); 231 } 232 } 233 MyOutput.nxtln(); 234 MyOutput.clrscr(); 235#ifndef _dutch 236 MyOutput.txt("Choice= "); 237#else 238 MyOutput.txt("Keuze= "); 239#endif 240 MyOutput.nxtln(); 241 MyOutput.txt(Tasks[ans]); 242 delay(1000); 243 return ans; 244} 245 246 247int getSide() { 248 249#ifndef _dutch 250 const String Txts[] = {"Turn ", 251 " side"}; 252 const String SidesTxt[] = {"ground", 253 "front ", 254 " top ", 255 " back ", 256 " left ", 257 "right "}; 258 259#else 260 const String Txts[] = {"Draai ", 261 "kant"}; 262 263 const String SidesTxt[] = {" onder", 264 " voor", 265 " boven", 266 "achter", 267 "linker", 268 "rechter"}; 269#endif 270 271 MyOutput.nxtln(); 272 MyOutput.clrscr(); 273#ifndef _dutch 274 MyOutput.txt("Choose side/axis>"); 275#else 276 MyOutput.txt("Kies zijkant/as>"); 277#endif 278 MyOutput.nxtln(); 279 MyOutput.txt(Txts[0]); 280 int ans = MyCube.frontSide; 281 MyOutput.txt(SidesTxt[ans]); 282 MyOutput.txt(Txts[1]); 283 284 while (!confirmed()) { 285 if (RightPanel.getOption() == RightPanel.highBut) { 286 escFlag = true; 287 return; 288 } 289 int old_ans = ans; 290 int userInput = LeftPanel.getOption(); 291 if (ans > -1 && ans < 4) { 292 if (userInput == LeftPanel.arrowUp) { 293 ans++; 294 if (ans == 4) { 295 ans = MyCube.groundSide; 296 } 297 } 298 if (userInput == LeftPanel.arrowDown) { 299 ans--; 300 if (ans == -1) { 301 ans = MyCube.backSide; 302 } 303 } 304 if (userInput == LeftPanel.arrowLeft) { 305 ans = MyCube.leftSide; 306 } 307 if (userInput == LeftPanel.arrowRight) { 308 ans = MyCube.rightSide; 309 } 310 } 311 if (ans == MyCube.leftSide) { 312 if (userInput == LeftPanel.arrowRight) { 313 ans = MyCube.frontSide; 314 } 315 if (userInput == LeftPanel.arrowUp) { 316 ans = MyCube.topSide; 317 } 318 if (userInput == LeftPanel.arrowDown) { 319 ans = MyCube.groundSide; 320 } 321 } 322 if (ans == MyCube.rightSide) { 323 if (userInput == LeftPanel.arrowLeft) { 324 ans = MyCube.frontSide; 325 } 326 if (userInput == LeftPanel.arrowUp) { 327 ans = MyCube.topSide; 328 } 329 if (userInput == LeftPanel.arrowDown) { 330 ans = MyCube.groundSide; 331 } 332 } 333 if (userInput == LeftPanel.arrowCentre) { 334 ans = MyCube.frontSide; 335 } 336 if (ans != old_ans) { 337 MyOutput.nxtln(); 338 MyOutput.txt(Txts[0]); 339 MyOutput.txt(SidesTxt[ans]); 340 MyOutput.txt(Txts[1]); 341 delay(100); 342 } 343 } 344 MyOutput.clrscr(); 345#ifndef _dutch 346 MyOutput.txt("Choice= "); 347#else 348 MyOutput.txt("Keuze = "); 349#endif 350 MyOutput.nxtln(); 351 MyOutput.txt(Txts[0]); 352 MyOutput.txt(SidesTxt[ans]); 353 MyOutput.txt(Txts[1]); 354 delay(1000); 355 return ans; 356} 357 358 359bool getDir() { 360 361#ifndef _dutch 362 const String DirTxt[] = {"Left turn", 363 "Right turn"}; 364#else 365 const String DirTxt[] = {"Linksom ", 366 "Rechtsom"}; 367#endif 368 bool CW = 1; 369 bool ans = CW; 370 371 MyOutput.clrscr(); 372#ifndef _dutch 373 MyOutput.txt("Choose direction>"); 374#else 375 MyOutput.txt("Kies richting>"); 376#endif 377 MyOutput.nxtln(); 378 MyOutput.txt(DirTxt[ans]); 379 while (!confirmed()) { 380 if (RightPanel.getOption() == RightPanel.highBut) { 381 escFlag = true; 382 return; 383 } 384 bool old_ans = ans; 385 int userInput = LeftPanel.getOption(); 386 if (userInput != 0) { 387 ans = !ans; 388 } 389 if (ans != old_ans) { 390 MyOutput.nxtln(); 391 MyOutput.txt(DirTxt[ans]); 392 delay(100); 393 } 394 } 395 MyOutput.clrscr(); 396#ifndef _dutch 397 MyOutput.txt("Direction="); 398#else 399 MyOutput.txt("Richting="); 400#endif 401 MyOutput.nxtln(); 402 MyOutput.txt(DirTxt[ans]); 403 delay(1000); 404 return ans; 405} 406 407 408bool confirmed() { 409 int UserInput = RightPanel.getOption(); 410 if (UserInput == RightPanel.lowBut) { 411 return 1; 412 } 413 return 0; 414} 415
choices.h
c_cpp
Reads User input from homemade input panel.
1/* 2 Choices.h - Library for multiple choice via analog input 3 Created by Koen Meesters, February 21st 2019 4 Last edited by Koen Meesters, February 21st 2019 5*/ 6#ifndef choices_h 7#define choices_h 8 9#include "Arduino.h" 10 11class Choices 12{ 13 public: 14 Choices(int AIpin, int numOpt); 15 int getOption(); 16 const int arrowUp = 1; 17 const int arrowLeft = 2; 18 const int arrowCentre = 3; 19 const int arrowRight = 4; 20 const int arrowDown = 5; 21 const int highBut = 1; 22 const int midBut = 2; 23 const int lowBut = 3; 24 25 private: 26 int _AIpin; 27 int _numOpt; 28 int readValue(); 29}; 30 31#endif 32
cube.cpp
c_cpp
Cube: Library that represents and manipulates Cube
1/* 2 Cube.h - Library for Rubik's Cube 3 Created by Koen Meesters, March 5th 2019 4 Last edited by Koen Meesters, 5*/ 6#define LED_COUNT 54 7 8#include "Arduino.h" 9#include "cube.h" 10#include <PololuLedStrip.h> 11 12 13PololuLedStrip<2> ledStrip; //Sturing ledstrip op DO 2 14 15Cube::Cube(int) { 16 17 int _noIdeaWhy=noIdeaWhy; 18 19 //initialize cube 20 for (int curSide = 0; curSide < numSides; curSide++) { //fill all sides 21 for (int curPos = 0; curPos < numPos; curPos++) { //fill all positions 22 Color[curSide][curPos] = 10*curSide + curPos; 23 } 24 } 25 26 //initialize Rims 01 02 03 04 05 06 07 08 09 10 11 12 27 byte rims[6][12][2] = {{{1,7},{1,6},{1,5},{5,7},{5,6},{5,5},{3,3},{3,2},{3,1},{4,7},{4,6},{4,5}}, //rims side 0 28 {{2,7},{2,6},{2,5},{5,1},{5,8},{5,7},{0,3},{0,2},{0,1},{4,5},{4,4},{4,3}}, //rims side 1 29 {{3,7},{3,6},{3,5},{5,3},{5,2},{5,1},{1,3},{1,2},{1,1},{4,3},{4,2},{4,1}}, //rims side 2 30 {{0,7},{0,6},{0,5},{5,5},{5,4},{5,3},{2,3},{2,2},{2,1},{4,1},{4,8},{4,7}}, //rims side 3 31 32 {{2,1},{2,8},{2,7},{1,1},{1,8},{1,7},{0,1},{0,8},{0,7},{3,1},{3,8},{3,7}}, //rims side 4 33 {{2,5},{2,4},{2,3},{3,5},{3,4},{3,3},{0,5},{0,4},{0,3},{1,5},{1,4},{1,3}}};//rims side 5 34 35 for (int curSide = 0; curSide < numSides; curSide++) { 36 for (int curRimPos = 0; curRimPos < numRimPos; curRimPos++) { 37 const int side=0; //element index of Side in rims 38 const int pos=1; //element index of Position in rims 39 rimsPtr[curSide][curRimPos] = &Color[rims[curSide][curRimPos][side]][rims[curSide][curRimPos][pos]]; 40 } 41 } 42 43 //intitialize Mids 01 02 03 04 05 06 07 08 09 10 11 12 44 byte mids[6][12][2] = {{{1,8},{1,0},{1,4},{5,8},{5,0},{5,4},{3,4},{3,0},{3,8},{4,8},{4,0},{4,4}}, //mids side 0 45 {{2,8},{2,0},{2,4},{5,2},{5,0},{5,6},{0,4},{0,0},{0,8},{4,6},{4,0},{4,2}}, //mids side 1 46 {{3,8},{3,0},{3,4},{5,4},{5,0},{5,8},{1,4},{1,0},{1,8},{4,4},{4,0},{4,8}}, //mids side 2 47 {{0,8},{0,0},{0,4},{5,6},{5,0},{5,2},{2,4},{2,0},{2,8},{4,2},{4,0},{4,6}}, //mids side 3 48 49 {{2,2},{2,0},{2,6},{1,2},{1,0},{1,6},{0,2},{0,0},{0,6},{3,2},{3,0},{3,6}}, //mids side 4 50 {{2,6},{2,0},{2,2},{3,6},{3,0},{3,2},{0,6},{0,0},{0,2},{1,6},{1,0},{1,2}}};//mids side 5 51 52 for (int curSide = 0; curSide < numSides; curSide++) { 53 for (int curMidPos = 0; curMidPos < numMidPos; curMidPos++) { 54 const int side=0; //element index of Side in mids 55 const int pos=1; //element index of Position in mids 56 midsPtr[curSide][curMidPos] = &Color[mids[curSide][curMidPos][side]][mids[curSide][curMidPos][pos]]; 57 } 58 } 59} 60/* 61 Each side has 9 positions 62 [ 1][ 2][ 3] 63 [ 8][ 0][ 4] 64 [ 7][ 6][ 5] 65 66 6 sides together form a cube Ledstripn addresses 67 0 ground 68 [20] [32] [44] 69 [19] [31] [43] 70 [18] [30] [42] 71 3 Back 3 back 72 [ 1][ 2][ 3] [17] [29] [41] 73 [ 8][ 0][ 4] [16] [28] [40] 74 [ 7][ 6][ 5] [15] [27] [39] 75 2 Top 2 Top 76 [ 1][ 2][ 3] [14] [26] [38] 77 [ 8][ 0][ 4] [13] [25] [37] 78 [ 7][ 6][ 5] [12] [24] [36] 79 4 Left 1 Front 5 Right 4 Left 1 Front 5 Right 80 [ 1][ 2][ 3] [ 1][ 2][ 3] [ 1][ 2][ 3] [ 2] [ 3] [ 8] [11] [23] [35] [53] [48] [47] 81 [ 8][ 0][ 4] [ 8][ 0][ 4] [ 8][ 0][ 4] [ 1] [ 4] [ 7] [10] [22] [34] [52] [49] [46] 82 [ 7][ 6][ 5] [ 7][ 6][ 5] [ 7][ 6][ 5] [ 0] [ 5] [ 6] [ 9] [21] [33] [51] [50] [45] 83 0 Ground 84 [ 1][ 2][ 3] 85 [ 8][ 0][ 4] 86 [ 7][ 6][ 5] 87 88 89 //vertaaltqbel cube naar Ledstrip 90 31,20,32,44,43,42,30,18,19, 91 22,11,23,35,34,33,21, 9,10, 92 25,14,26,38,37,36,24,12,13, 93 28,17,29,41,40,39,27,15,16, 94 4, 2, 3, 8, 7, 6, 5, 0, 1, 95 49,53,48,47,46,45,50,51,52 96 97 98 Each side has a number and a character ID 99 0 = groundSide 100 1 = frontSide 101 2 = topSide 102 3 = backSide 103 4 = leftSide 104 5 = rightSide 105 106 The cube will have 6 * 9 = 54 RGB LEDS, so 162 LED's! 107*/ 108 109//This function checks if the cube is solved (each side has one color) 110bool Cube::checkCubeSolved() { 111 for (int curSide = 0; curSide < numSides; curSide++) { //check all sides 112 byte sideColor = Color[curSide][0]/10; 113 for (int curPos = 1; curPos < numPos; curPos++) { //fill all positions 114 if (!(Color[curSide][curPos]/10==sideColor)) return false; 115 } 116 } 117 return true; 118} 119 120//This procedure reinitializes the cube 121void Cube::reInit() { 122 for (int curSide = 0; curSide < numSides; curSide++) { //fill all sides 123 for (int curPos = 0; curPos < numPos; curPos++) { //fill all positions 124 Color[curSide][curPos] = 10*curSide + curPos; //later colours will be set here 125 } 126 } 127 show(); 128} 129 130//This procedure does a little show 131void Cube::littleShow() { 132 delay(1000); 133 for (int i=0; i<4; i++) { 134 showTurnMid(i,!CW); 135 delay(1000); 136 } 137 for (int i=3; i>=0; i--) { 138 showTurnMid(i,CW); 139 delay(1000); 140 } 141 delay(1000); 142 reInit(); 143} 144 145//This procedure shows a turn of the cube as a whole 146void Cube::showTurnCube(int side,bool dir) { 147 showTurnSide(side,dir); 148 showTurnMid(side,dir); 149 int oppSide = 0; 150 if (side<2) oppSide = side+2; 151 if (side>1 && side<4) oppSide = side-2; 152 if (side==4) oppSide = 5; 153 if (side==5) oppSide = 4; 154 showTurnSide(oppSide,!dir); 155 delay(delta_t); 156} 157 158//This procedure shows a turn of a side 159void Cube::showTurnSide(int side,bool dir) { 160 turnRim(side,dir); 161 show(); 162 delay(delta_t); 163 for (int i = 0; i<2; i++) { 164 turnSide(side,dir); 165 show(); 166 delay(delta_t); 167 turnRim(side,dir); 168 show(); 169 delay(delta_t); 170 } 171} 172 173//This procedrue shows a turn of the mids 174void Cube::showTurnMid(int side,bool dir) { 175 for (int i = 0; i<3; i++) { 176 turnMid(side,dir); 177 show(); 178 delay(delta_t); 179 } 180} 181 182//This procedure turns the side 183void Cube::turnSide(int side, bool dir) { //this procedure must be run 2 times per turn 184 byte tempStoreColor; 185 if (dir == CW) { //turn ClockWise 186 tempStoreColor = Color[side][numPos-1]; //Color value of Position 8 is temporarily stored 187 for (int curPos = numPos-2; curPos > 0; curPos--) { 188 Color[side][curPos+1] = Color[side][curPos]; //shift Color value Clockwise 189 } 190 Color[side][1] = tempStoreColor; //put stored Color value in Position 1 191 } 192 else { //turn Counter ClockWise 193 tempStoreColor = Color[side][1]; //Color value of Position 1 is temporarily stored 194 for (int curPos = 1; curPos < numPos-1; curPos++) { 195 Color[side][curPos] = Color[side][curPos+1]; //shift Color value counter Clockwise 196 } 197 Color[side][numPos-1] = tempStoreColor; //put stored Color value in Position 8 198 } 199} 200 201//this procedure turns de mids 202void Cube::turnMid(int side, bool dir) { //this procedure must be run 3 times per turn 203 byte tempStoreColor; 204 if (dir == CW) { //if turn ClockWise 205 tempStoreColor = *midsPtr[side][numRimPos-1]; //Color value of Position 12 is temporarily stored 206 for (int curPos = numMidPos-1; curPos > 0; curPos--) { 207 *midsPtr[side][curPos] = *midsPtr[side][curPos-1]; //shift Color value Clockwise 208 } 209 *midsPtr[side][0] = tempStoreColor; //put stored Color value in Position 0 210 } 211 else { //turn Counter ClockWise 212 tempStoreColor = *midsPtr[side][0]; //Color value of Position 0 is temporarily stored 213 for (int curPos = 0; curPos < numMidPos-1; curPos++) { 214 *midsPtr[side][curPos] = *midsPtr[side][curPos+1]; //shift Color value Clockwise 215 } 216 *midsPtr[side][numRimPos-1] = tempStoreColor; //put stored Color value in Position 12 217 } 218} 219 220//This procedure turns the rims of a Side 221void Cube::turnRim(int side, bool dir) { //this procedure must be run 3 times per turn 222 byte tempStoreColor; 223 if (dir == CW) { //turn ClockWise 224 tempStoreColor = *rimsPtr[side][numRimPos-1]; //Color value of Position 12 is temporarily stored 225 for (int curPos = numRimPos-1; curPos > 0; curPos--) { 226 *rimsPtr[side][curPos] = *rimsPtr[side][curPos-1]; //shift Color value Clockwise 227 } 228 *rimsPtr[side][0] = tempStoreColor; //put stored Color value in Position 0 229 } 230 else { //turn counter clockwise 231 tempStoreColor = *rimsPtr[side][0]; //Color value of Position 0 is temporarily stored 232 for (int curPos = 0; curPos < numRimPos-1; curPos++) { 233 *rimsPtr[side][curPos] = *rimsPtr[side][curPos+1]; //shift Color value counter clockwise 234 } 235 *rimsPtr[side][numRimPos-1] = tempStoreColor; //put stored Color value in Position 12 236 } 237} 238 239//This function hands the address of the cube to the ledstrip writing procedure 240const byte* Cube::handCube() { 241 return &Color[0][0]; 242} 243 244//#define _debugging 245#ifdef _debugging 246//This procedure writes the Cube colors to Serial (for debugging purposes) 247void Cube::toScreen() { 248 Serial.println("Cube"); 249 int transPos[] = { 1,2,3, 250 8,0,4, 251 7,6,5 }; 252 int curSide; 253 254 //backSide and TopSide 255 for (curSide = backSide; curSide >= topSide; curSide--) { 256 for (int i=0; i<3; i++) { // voor alle posities 257 Serial.print(" "); 258 for (int j=(3*i); j<(3+3*i); j++) { 259 int pos = transPos[j]; 260 Serial.print('['); 261 if(Color[curSide][pos]<9) {Serial.print(' ');} 262 Serial.print(Color[curSide][pos]); 263 Serial.print("] "); 264 } 265 Serial.println(); 266 } 267 Serial.println(); 268 } 269 270 //leftSide, frontSide and rightSide 271 for (int i=0; i<3; i++) { // voor alle posities 272 curSide = leftSide; 273 for (int j=(3*i); j<(3+3*i); j++) { 274 int pos = transPos[j]; 275 Serial.print('['); 276 if(Color[curSide][pos]<9) {Serial.print(' ');} 277 Serial.print(Color[curSide][pos]); 278 Serial.print("] "); 279 } 280 Serial.print(' '); 281 curSide = frontSide; 282 for (int j=(3*i); j<(3+3*i); j++) { 283 int pos = transPos[j]; 284 Serial.print('['); 285 if(Color[curSide][pos]<9) {Serial.print(' ');} 286 Serial.print(Color[curSide][pos]); 287 Serial.print("] "); 288 } 289 Serial.print(' '); 290 curSide = rightSide; 291 for (int j=(3*i); j<(3+3*i); j++) { 292 int pos = transPos[j]; 293 Serial.print('['); 294 if(Color[curSide][pos]<9) {Serial.print(' ');} 295 Serial.print(Color[curSide][pos]); 296 Serial.print("] "); 297 } 298 Serial.println(); 299 } 300 Serial.println(); 301 302 //groundSide 303 curSide = groundSide; 304 for (int i=0; i<3; i++) { // voor alle posities 305 Serial.print(" "); 306 for (int j=(3*i); j<(3+3*i); j++) { 307 int pos = transPos[j]; 308 Serial.print('['); 309 if(Color[curSide][pos]<9) {Serial.print(' ');} 310 Serial.print(Color[curSide][pos]); 311 Serial.print("] "); 312 } 313 Serial.println(); 314 } 315 Serial.println(); 316} 317#endif 318 319/* alternative Cube config 320 * 321 * 51 52 53 322 * 58 50 54 323 * 57 56 55 324 * 325 * 31 32 33 41 42 43 326 * 38 30 34 48 40 44 327 * 37 36 35 47 46 45 328 * 329 * 11 12 13 21 22 23 330 * 18 10 14 28 20 24 331 * 17 16 15 27 26 25 332 * 333 * 01 02 03 334 * 08 00 04 335 * 07 06 05 336 * 337 *rims 338 *0: 17 16 15 27 26 25 339 *1: 31 38 37 21 28 27 03 02 01 340 *2: 37 36 35 47 46 45 05 04 03 15 14 13 341 *3: 51 58 57 41 48 47 23 22 21 13 12 11 342 *4: 57 56 55 25 24 23 35 34 33 343 *5: 43 42 41 33 32 31 344 * 345 *mids 346 *0: 18 10 14 28 20 24 347 *1: 32 30 36 22 20 26 04 00 08 348 *2: 38 30 34 48 40 44 06 00 02 16 10 12 349 *3: 52 50 56 42 40 46 24 20 28 14 10 18 350 *4: 58 50 54 26 20 22 36 30 32 351 *5: 44 40 48 34 30 38 352 * 353 * 354 */ 355 356 void Cube::dark() { 357 rgb_color Dark; 358 Dark.red = 0; 359 Dark.green = 0; 360 Dark.blue = 0; 361 362 rgb_color colors[54]; 363 for (int i=0; i<54; i++) { 364 colors[i] = Dark; 365 } 366 ledStrip.write(colors, LED_COUNT); 367 } 368 369void Cube::show() { 370 371 rgb_color Red; 372 Red.red = 10; 373 Red.green = 0; 374 Red.blue = 0; 375 376 rgb_color Blue; 377 Blue.red = 0; 378 Blue.green = 0; 379 Blue.blue = 10; 380 381 rgb_color Green; 382 Green.red = 0; 383 Green.green = 10; 384 Green.blue = 0; 385 386 rgb_color Yellow; 387 Yellow.red = 60; 388 Yellow.green = 45; 389 Yellow.blue = 0; 390 391 rgb_color White; 392 White.red = 20; 393 White.green = 20; 394 White.blue = 20; 395 396 rgb_color Orange; 397 Orange.red = 45; 398 Orange.green = 15; 399 Orange.blue = 0; 400 401 rgb_color Dark; 402 Dark.red = 0; 403 Dark.green = 0; 404 Dark.blue = 0; 405 406 rgb_color Colors[54]; 407 408 const byte ledStripAddr []= {31,20,32,44,43,42,30,18,19, 409 22,11,23,35,34,33,21, 9,10, 410 25,14,26,38,37,36,24,12,13, 411 28,17,29,41,40,39,27,15,16, 412 4, 2, 3, 8, 7, 6, 5, 0, 1, 413 49,53,48,47,46,45,50,51,52}; 414 415 for (int i=0; i<54; i++) { 416 Colors[i] = Dark; 417 } 418 419 const byte* cubePtr = handCube(); 420 for (int i = 0; i<54; i++) { 421 422 byte curColor = *cubePtr/10; 423 byte curLedStripAddr = ledStripAddr[i]; 424 switch (curColor) { 425 case 0: 426 Colors[curLedStripAddr] = Yellow; 427 break; 428 case 1: 429 Colors[curLedStripAddr] = Blue; 430 break; 431 case 2: 432 Colors[curLedStripAddr] = White; 433 break; 434 case 3: 435 Colors[curLedStripAddr] = Green; 436 break; 437 case 4: 438 Colors[curLedStripAddr] = Red; 439 break; 440 case 5: 441 Colors[curLedStripAddr] = Orange; 442 break; 443 } 444 cubePtr++; 445 } 446 ledStrip.write(Colors, LED_COUNT); 447} 448 449 450
output.h
c_cpp
Writes output to LCD and serial in one go (mainly handy for trouble shooting)
1/* 2 output.h - Library to handle serial and lcd screen output 3 Created by Koen Meesters, March 2019 4 Last edited by Koen Meesters, April 13th 2019 5*/ 6#ifndef output_h 7#define output_h 8 9#include "Arduino.h" 10#include <LiquidCrystal.h> 11 12class Output 13{ 14 public: 15 Output(bool serial,bool cryst); 16 int noIdeaWhy; 17 void txt(String); 18 void nxtln(); 19 void clrscr(); 20 private: 21 bool _serial; 22 bool _cryst; 23}; 24 25#endif 26
output.cpp
c_cpp
Writes output to LCD and serial in one go (mainly handy for trouble shooting)
1/* 2 output.cpp - Library to handle serial and lcd screen output 3 4 Created by Koen Meesters, March 2019 5 Last edited by Koen Meesters, April 6 13th 2019 7*/ 8 9#include "Arduino.h" 10#include "output.h" 11 12LiquidCrystal 13 lcd(8, 9, 10, 11, 12, 13); 14 15 16Output::Output(bool serial, bool cryst) { 17 18 lcd.begin(16,2); 19 _serial = serial; 20 _cryst = cryst; 21} 22 23void 24 Output::clrscr() { //clear screen (go to next line in Serial output) 25 26 if (_serial) { 27 Serial.println(); 28 } 29 if (_cryst) { 30 lcd.clear(); 31 32 } 33} 34 35void Output::txt(String text) { //write text 36 if (_serial) 37 { 38 Serial.print(text); 39 } 40 if (_cryst) { 41 lcd.print(text); 42 43 } 44} 45 46void Output::nxtln() { //go to next line 47 if (_serial) 48 { 49 Serial.println(); 50 } 51 if (_cryst) { 52 lcd.setCursor(0,1); 53 54 } 55} 56
choices.cpp
c_cpp
Reads User input from homemade input panel.
1/* 2 Choices.h - Library for multiple choice via analog input 3 Created 4 by Koen Meesters, February 21st 2019 5 Last edited by Koen Meesters, February 6 21st 2019 7*/ 8 9#include "Arduino.h" 10#include "choices.h" 11 12Choices::Choices(int 13 AIpin, int numOpt) 14{ 15 _AIpin = AIpin; 16 _numOpt = numOpt; 17} 18 19 20int 21 Choices::getOption() { 22 bool valid = 0; 23 24 while (!valid) { 25 int 26 value = readValue(); 27 28 const int maxValue = 1024; 29 int stepSize = 30 maxValue/(_numOpt+1); 31 int validZone = stepSize/4; 32 33 for (int curOpt 34 = 0; curOpt <= _numOpt; curOpt++) { 35 if (value > curOpt*stepSize-validZone 36 && value < curOpt*stepSize+validZone) { 37 return(curOpt); 38 } 39 40 } 41 } 42} 43 44 45int Choices::readValue(){ 46 const int numReads = 47 10; //Value is measured multiple times 48 analogReference(EXTERNAL); 49 int 50 totVal = 0; 51 int lowest = 1024; 52 int highest = 0; 53 delay(10); 54 55 for (int i = 0; i < numReads; i++) { 56 int curVal = analogRead(_AIpin); 57 58 if (curVal < lowest) lowest = curVal; 59 if (curVal > highest) highest = 60 curVal; 61 totVal += curVal; 62 delay(10); 63 } 64 int value = totVal/numReads; 65 66 if ((highest - lowest)>50) value = 0; 67 return(value); 68} 69
cube.h
c_cpp
Cube: Library that represents and manipulates Cube
1/* 2 Cube.h - Library for Rubik's Cube 3 Created by Koen Meesters, 4 March 5th 2019 5 Last edited by Koen Meesters, 6*/ 7#ifndef cube_h 8#define 9 cube_h 10 11#include "Arduino.h" 12#include <PololuLedStrip.h> 13 14 15class 16 Cube 17{ 18 public: 19 Cube(int); 20 int noIdeaWhy; //somehow 21 a class cannot have a constructor without an arguement... 22 void reInit(); 23 //reinitializes the Cube 24 void show(); 25 void dark(); 26 27 void showTurnSide(int side,bool dir); 28 void showTurnMid(int side,bool 29 dir); 30 void showTurnCube(int side,bool dir); 31 bool checkCubeSolved(); 32 //checks if the cube is solved 33 void littleShow(); //gives 34 a little show 35 void toScreen(); //writes Cube to computer 36 screen via Serial 37 const byte* handCube(); //hands Cube to main 38 program 39 40 static const int numSides = 6; //Cube has 6 sides 41 42 43 const int backSide = 3; 44 const int topSide = 2; 45 const int 46 frontSide = 1; 47 const int groundSide = 0; 48 const int leftSide = 4; 49 50 const int rightSide = 5; 51 52 static const int numPos = 9; //Each 53 side has 9 positions //needs to be static zijn as number of array elements needs 54 to be fixed before compilation 55 56 const bool CW = true; 57 58 59 60 private: 61 62 void turnSide(int, bool); //turns a side of Cube 63 64 void turnRim(int, bool); //turns rim of a side of Cube 65 void 66 turnMid(int, bool); //turns the mids of of Cube 67 int _noIdeaWhy; 68 69 byte Color[numSides][numPos]; //dach position has a color that will 70 be represented in a byte 71 static const int numRimPos=12; //dach rim 72 has 12 positions 73 static const int numMidPos=12; //each mid has 12 74 positions 75 byte* rimsPtr[numSides][numRimPos]; //array of pointers to addresses 76 of the rim positions of a Side, used in turnRim() 77 byte* midsPtr[numSides][numRimPos]; 78 //array of pointers to addresses of the mids positions of a Side, used in turnMid() 79 80 byte delta_t = 150; //gives good visuals 81}; 82#endif 83
output.cpp
c_cpp
Writes output to LCD and serial in one go (mainly handy for trouble shooting)
1/* 2 output.cpp - Library to handle serial and lcd screen output 3 Created by Koen Meesters, March 2019 4 Last edited by Koen Meesters, April 13th 2019 5*/ 6 7#include "Arduino.h" 8#include "output.h" 9 10LiquidCrystal lcd(8, 9, 10, 11, 12, 13); 11 12 13Output::Output(bool serial, bool cryst) { 14 lcd.begin(16,2); 15 _serial = serial; 16 _cryst = cryst; 17} 18 19void Output::clrscr() { //clear screen (go to next line in Serial output) 20 if (_serial) { 21 Serial.println(); 22 } 23 if (_cryst) { 24 lcd.clear(); 25 } 26} 27 28void Output::txt(String text) { //write text 29 if (_serial) { 30 Serial.print(text); 31 } 32 if (_cryst) { 33 lcd.print(text); 34 } 35} 36 37void Output::nxtln() { //go to next line 38 if (_serial) { 39 Serial.println(); 40 } 41 if (_cryst) { 42 lcd.setCursor(0,1); 43 } 44} 45
choices.cpp
c_cpp
Reads User input from homemade input panel.
1/* 2 Choices.h - Library for multiple choice via analog input 3 Created by Koen Meesters, February 21st 2019 4 Last edited by Koen Meesters, February 21st 2019 5*/ 6 7#include "Arduino.h" 8#include "choices.h" 9 10Choices::Choices(int AIpin, int numOpt) 11{ 12 _AIpin = AIpin; 13 _numOpt = numOpt; 14} 15 16 17int Choices::getOption() { 18 bool valid = 0; 19 20 while (!valid) { 21 int value = readValue(); 22 23 const int maxValue = 1024; 24 int stepSize = maxValue/(_numOpt+1); 25 int validZone = stepSize/4; 26 27 for (int curOpt = 0; curOpt <= _numOpt; curOpt++) { 28 if (value > curOpt*stepSize-validZone && value < curOpt*stepSize+validZone) { 29 return(curOpt); 30 } 31 } 32 } 33} 34 35 36int Choices::readValue(){ 37 const int numReads = 10; //Value is measured multiple times 38 analogReference(EXTERNAL); 39 int totVal = 0; 40 int lowest = 1024; 41 int highest = 0; 42 delay(10); 43 for (int i = 0; i < numReads; i++) { 44 int curVal = analogRead(_AIpin); 45 if (curVal < lowest) lowest = curVal; 46 if (curVal > highest) highest = curVal; 47 totVal += curVal; 48 delay(10); 49 } 50 int value = totVal/numReads; 51 if ((highest - lowest)>50) value = 0; 52 return(value); 53} 54
cube.h
c_cpp
Cube: Library that represents and manipulates Cube
1/* 2 Cube.h - Library for Rubik's Cube 3 Created by Koen Meesters, March 5th 2019 4 Last edited by Koen Meesters, 5*/ 6#ifndef cube_h 7#define cube_h 8 9#include "Arduino.h" 10#include <PololuLedStrip.h> 11 12 13class Cube 14{ 15 public: 16 Cube(int); 17 int noIdeaWhy; //somehow a class cannot have a constructor without an arguement... 18 void reInit(); //reinitializes the Cube 19 void show(); 20 void dark(); 21 void showTurnSide(int side,bool dir); 22 void showTurnMid(int side,bool dir); 23 void showTurnCube(int side,bool dir); 24 bool checkCubeSolved(); //checks if the cube is solved 25 void littleShow(); //gives a little show 26 void toScreen(); //writes Cube to computer screen via Serial 27 const byte* handCube(); //hands Cube to main program 28 29 static const int numSides = 6; //Cube has 6 sides 30 31 const int backSide = 3; 32 const int topSide = 2; 33 const int frontSide = 1; 34 const int groundSide = 0; 35 const int leftSide = 4; 36 const int rightSide = 5; 37 38 static const int numPos = 9; //Each side has 9 positions //needs to be static zijn as number of array elements needs to be fixed before compilation 39 40 const bool CW = true; 41 42 43 private: 44 45 void turnSide(int, bool); //turns a side of Cube 46 void turnRim(int, bool); //turns rim of a side of Cube 47 void turnMid(int, bool); //turns the mids of of Cube 48 int _noIdeaWhy; 49 byte Color[numSides][numPos]; //dach position has a color that will be represented in a byte 50 static const int numRimPos=12; //dach rim has 12 positions 51 static const int numMidPos=12; //each mid has 12 positions 52 byte* rimsPtr[numSides][numRimPos]; //array of pointers to addresses of the rim positions of a Side, used in turnRim() 53 byte* midsPtr[numSides][numRimPos]; //array of pointers to addresses of the mids positions of a Side, used in turnMid() 54 byte delta_t = 150; //gives good visuals 55}; 56#endif 57
Downloadable files
Rubik's Cube Bread Board
Rubik's Cube Bread Board
Rubiks's Cube electrical scheme
Rubiks's Cube electrical scheme
Rubik's Cube Bread Board
Rubik's Cube Bread Board
Comments
Only logged in users can leave comments