Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI

on:
push:
branches: [master]
pull_request:

permissions:
contents: read

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install package
run: python -m pip install -e .
- name: Compile Python source
run: python -m compileall -q src tests
- name: Run unit tests
run: python -m unittest discover -s tests -v
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
__pycache__/
*.py[cod]
.coverage
.pytest_cache/
.venv/
venv/
dist/
build/
*.egg-info/
.DS_Store
.idea/
.vscode/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 RubikSolverRobot contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
121 changes: 118 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,119 @@
# RubikSolverRobot

![](https://github.com/contursofran/RubikSolverRobot/blob/master/Avance%201.png)
![](https://github.com/contursofran/RubikSolverRobot/blob/master/Avance%202.png)
![](https://github.com/contursofran/RubikSolverRobot/blob/master/Avance%203.png)
An archived robotics project that detects a Rubik's Cube, calculates a solution,
and sends face-turn commands to an Arduino-driven mechanism.

The original prototype is preserved in [`legacy/`](legacy/). A small Python 3
command-line interface now documents and tests the core solver-to-robot protocol
without starting a camera or moving hardware by default.

> [!IMPORTANT]
> This is a historical showcase project, not a maintained hardware product.
> Servo geometry, camera thresholds, and timing values depend on the original
> prototype. Test all motion with the mechanism disconnected or unloaded first.

## What the prototype did

1. Captured the six cube faces with a webcam.
2. Classified sticker colors with OpenCV thresholds.
3. Used the Kociemba algorithm to calculate a solution.
4. Converted standard cube moves to a one-byte serial protocol.
5. Used an Arduino and two servos to reposition and turn the cube.

```mermaid
flowchart LR
Camera[Webcam] --> Vision[Color detection]
Vision --> State[54-sticker cube state]
State --> Solver[Kociemba solver]
Solver --> Protocol[Move encoder]
Protocol --> Serial[USB serial]
Serial --> Arduino[Arduino and servos]
```

## Repository map

| Path | Purpose |
| --- | --- |
| `src/rubik_solver_robot/` | Safe Python 3 state validation, solving, and command encoding |
| `firmware/rubik_robot/` | Serial protocol test sketch; it does not move servos |
| `legacy/` | Unmodified original Python GUI and Arduino sketch |
| `docs/` | Architecture, hardware notes, and legacy limitations |
| `tests/` | Unit tests for cube-state and protocol rules |

## Try it without hardware

Python 3.10 or later is required.

```bash
python -m pip install -e .
rubik-robot --solution "R U R' U'"
```

The command prints the encoded bytes and does not open a serial port:

```text
Moves: R U R' U'
Commands: R U r u
Mode: dry run (no serial data sent)
```

You can also solve a 54-character cube state in `URFDLB` face order:

```bash
python -m pip install -e '.[solver]'
rubik-robot --cube-state UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB
```

Hardware execution is an explicit action:

```bash
python -m pip install -e '.[hardware]'
rubik-robot --solution "R U R' U'" --execute --port /dev/ttyACM0
```

Use a Windows port such as `COM5` when applicable. Read
[`docs/HARDWARE.md`](docs/HARDWARE.md) before you send commands.

## Protocol

The modern interface accepts standard moves for the six faces: `U`, `R`, `F`,
`D`, `L`, and `B`. A prime suffix means a counter-clockwise turn. A `2` suffix
means a half turn.

| Move | Serial byte | Move | Serial byte | Move | Serial byte |
| --- | --- | --- | --- | --- | --- |
| `F` | `F` | `F'` | `f` | `F2` | `1` |
| `B` | `B` | `B'` | `b` | `B2` | `2` |
| `U` | `U` | `U'` | `u` | `U2` | `3` |
| `D` | `D` | `D'` | `d` | `D2` | `4` |
| `L` | `L` | `L'` | `l` | `L2` | `5` |
| `R` | `R` | `R'` | `r` | `R2` | `6` |

## Historical screenshots

![Cube detection interface](docs/images/progress-1.png)

![Detected face and controls](docs/images/progress-2.png)

![Multi-face capture](docs/images/progress-3.png)

## Current limits

- The Python 3 interface does not replace the old camera GUI.
- The protocol test firmware does not drive the servo mechanism.
- The legacy Arduino sketch has known syntax and dispatch defects.
- The original color thresholds and motion values need hardware calibration.
- Exact compatibility with the original prototype is not a project goal.

See [`docs/LEGACY_NOTES.md`](docs/LEGACY_NOTES.md) for the audit details.

## Development

```bash
python -m unittest discover -s tests -v
python -m compileall -q src tests
```

## License

This repository is available under the [MIT License](LICENSE).
25 changes: 25 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Architecture

## Modern archival interface

The Python 3 code separates the stable parts of the project into small modules:

- `cube.py` validates a 54-sticker state and calls the optional Kociemba solver.
- `protocol.py` validates face-turn notation and converts each move to one byte.
- `controller.py` opens a serial port only after the caller requests execution.
- `cli.py` combines these parts and uses dry-run mode by default.

The interface has no camera dependency. This makes the protocol easy to review,
test, and demonstrate on a computer that has no robot attached.

## Safety boundary

Parsing and dry runs cannot open a serial port. Hardware access requires both
`--execute` and `--port`. This boundary prevents an accidental servo movement
during normal examples and automated tests.

## Legacy application

The original Python program contains the full camera pipeline and Tkinter UI.
It also creates global camera and UI resources at module load time. It remains
in `legacy/` as source material. It is not imported by the modern package.
32 changes: 32 additions & 0 deletions docs/HARDWARE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Hardware notes

## Historical setup

The source indicates that the prototype used:

- an Arduino-compatible board;
- two hobby servos on pins 3 and 4;
- a USB webcam;
- a serial connection at 9600 baud;
- a mechanical arm that pushes and holds the cube;
- a rotating base that changes cube orientation.

The repository does not include a bill of materials, wiring diagram, CAD files,
or servo specifications. Do not infer electrical limits from the source code.

## Safe test sequence

1. Disconnect servo power.
2. Upload the protocol test sketch from `firmware/rubik_robot/`.
3. Run the Python CLI without `--execute` and inspect its command output.
4. Connect to the test sketch and confirm the `ACK` lines in the serial monitor.
5. Review the original motion code in `legacy/arduino_moves.ino`.
6. Restore motion one operation at a time and calibrate mechanical limits.
7. Keep the cube and hands clear during powered tests.

## Firmware status

The modern sketch validates the serial protocol only. It does not attach servos
or move the mechanism. The old sketch contains the historical motion sequences,
but it has known compile and command-dispatch defects. It needs repair and
hardware-specific calibration before use.
33 changes: 33 additions & 0 deletions docs/LEGACY_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Legacy audit notes

The `legacy/` directory preserves the original uploaded files without a code
rewrite. They show the complete prototype idea, but they are not a supported
runtime.

## Desktop application

- The file uses Python 2 `print` statements while importing Python 3 modules.
- Camera, GUI, solver, detection, and serial control share global state.
- The program creates a Tk root window and opens camera index 0 during import.
- The serial port is fixed to `COM5` at 9600 baud.
- Long hardware waits run in the same workflow as UI operations.
- Color detection uses fixed pixel regions, area limits, and HSV thresholds.
- The solve action refers to names that are not consistently defined.
- Dependencies and supported versions were not recorded.

## Arduino sketch

- At least one statement is missing a semicolon in the uploaded source.
- A function block near the end is not closed correctly.
- A lowercase `r` command calls the clockwise `R()` function.
- A one-byte variable is compared with the multi-character values `L2` and `R2`.
- The desktop code sends `5` and `6` for these half turns, but the sketch does
not handle those bytes.
- The loop prints a value even when no new serial byte is available.
- Servo angles and delays are fixed for one physical assembly.

## Publication decision

The modern package gives the repository a safe demonstration path. The old files
remain useful as a historical record. Exact restoration of the camera UI and
mechanical behavior is outside the archive scope.
File renamed without changes
File renamed without changes
File renamed without changes
38 changes: 38 additions & 0 deletions firmware/rubik_robot/rubik_robot.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RubikSolverRobot serial protocol test firmware.
//
// This sketch does not attach servos or move hardware. It lets you verify the
// desktop command stream before you restore and calibrate the legacy motion
// code.

const long SERIAL_BAUD_RATE = 9600;
const char VALID_COMMANDS[] = "Ff1Bb2Uu3Dd4Ll5Rr6S";

bool isValidCommand(char command) {
for (unsigned int index = 0; index < sizeof(VALID_COMMANDS) - 1; ++index) {
if (VALID_COMMANDS[index] == command) {
return true;
}
}
return false;
}

void setup() {
Serial.begin(SERIAL_BAUD_RATE);
Serial.println("READY RubikSolverRobot protocol test");
}

void loop() {
if (Serial.available() <= 0) {
return;
}

const char command = Serial.read();
if (!isValidCommand(command)) {
Serial.print("ERR ");
Serial.println(command);
return;
}

Serial.print("ACK ");
Serial.println(command);
}
File renamed without changes.
File renamed without changes.
28 changes: 28 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[build-system]
requires = ["setuptools>=69"]
build-backend = "setuptools.build_meta"

[project]
name = "rubik-solver-robot"
version = "1.0.0"
description = "Archived Rubik's Cube solver robot protocol tools"
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE" }
keywords = ["arduino", "robotics", "rubiks-cube", "serial"]
classifiers = [
"Development Status :: 7 - Inactive",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
]

[project.optional-dependencies]
solver = ["kociemba>=1.2.1,<2"]
hardware = ["pyserial>=3.5,<4"]
all = ["kociemba>=1.2.1,<2", "pyserial>=3.5,<4"]

[project.scripts]
rubik-robot = "rubik_solver_robot.cli:main"

[tool.setuptools.packages.find]
where = ["src"]
16 changes: 16 additions & 0 deletions src/rubik_solver_robot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Safe protocol tools for the archived RubikSolverRobot project."""

from .cube import SOLVED_STATE, CubeStateError, solve_cube, validate_cube_state
from .protocol import MoveError, encode_solution, parse_solution

__all__ = [
"SOLVED_STATE",
"CubeStateError",
"MoveError",
"encode_solution",
"parse_solution",
"solve_cube",
"validate_cube_state",
]

__version__ = "1.0.0"
3 changes: 3 additions & 0 deletions src/rubik_solver_robot/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .cli import main

raise SystemExit(main())
Loading
Loading