Esta librería permite controlar los autos de radio control (RC) de Shell Motorsport a través de Bluetooth Low Energy (BLE). Proporciona funciones para conectarse al vehículo, enviar comandos de movimiento y administrar múltiples autos.
- ✅ Control de autos RC vía Bluetooth Low Energy (BLE)
- ✅ Soporte para múltiples vehículos simultáneos
- ✅ Control con JoyCon de Nintendo Switch
- ✅ Mensajes precomputados para mejor rendimiento
- ✅ Manejo robusto de errores y reconexión
- ✅ Soporte para context managers (async with)
- ✅ Type hints completos
- ✅ Logging configurable
- ✅ Validación de comandos
pip install git+https://github.com/AMasetti/shell-motorsport-rc-lib-
Clona este repositorio:
git clone https://github.com/AMasetti/shell-motorsport-rc-lib.git
-
Navega al directorio del proyecto:
cd shell-motorsport-rc-lib -
Instala las dependencias requeridas:
pip install -r requirements.txt
Para usar JoyCon controllers, instala las dependencias adicionales:
pip install "shell_motorsport[joycon]"O manualmente:
pip install joycon-python hidapi pyglmimport asyncio
from shell_motorsport import ShellMotorsportCar
async def main():
car = ShellMotorsportCar()
try:
# Conectar por nombre (buscará y nombrará si no existe)
if "MI_AUTO" not in car.vehicle_list:
await car.find_and_name_car("MI_AUTO")
await car.connect_by_name("MI_AUTO")
# Mover hacia adelante por 1 segundo
await car.move_forward(duration=1.0)
# Mover hacia atrás
await car.move_backward(duration=1.0)
# Detener
await car.stop()
finally:
await car.disconnect()
if __name__ == "__main__":
asyncio.run(main())import asyncio
from shell_motorsport import ShellMotorsportCar
async def main():
async with ShellMotorsportCar() as car:
await car.connect_by_name("MI_AUTO")
await car.move_forward(duration=1.0)
# Desconexión automática al salir del bloque
asyncio.run(main())import asyncio
from shell_motorsport import ShellMotorsportCar
async def main():
car = ShellMotorsportCar()
try:
await car.connect_by_name("MI_AUTO")
# Mover hacia adelante
message = car.retrieve_precomputed_message(forward=1, speed=0x64)
await car.move_command(message)
await asyncio.sleep(1)
# Mover hacia atrás y a la izquierda
message = car.retrieve_precomputed_message(backward=1, left=1)
await car.move_command(message)
await asyncio.sleep(1)
await car.stop()
finally:
await car.disconnect()Para descubrir el device_id de tu auto y asignarle un nombre personalizado:
-
Enciende tu auto RC y asegúrate de que esté en modo de emparejamiento.
-
En tu script, usa
find_and_name_car():car = ShellMotorsportCar() await car.find_and_name_car("MI_AUTO")
-
El programa buscará dispositivos BLE cercanos, encontrará tu auto y guardará su
device_iden el archivovehicle_list.json. -
Luego puedes conectarte usando el nombre:
await car.connect_by_name("MI_AUTO")
La librería permite controlar múltiples autos RC simultáneamente:
import asyncio
from shell_motorsport import ShellMotorsportCar
async def main():
car1 = ShellMotorsportCar()
car2 = ShellMotorsportCar()
try:
await car1.connect_by_name("AUTO_1")
await car2.connect_by_name("AUTO_2")
# Controlar car1
await car1.move_forward(duration=1.0)
# Controlar car2
await car2.move_backward(duration=1.0)
finally:
await car1.disconnect()
await car2.disconnect()La librería soporta control de los autos RC utilizando los controladores JoyCon de Nintendo Switch. Puedes controlar múltiples autos simultáneamente usando los JoyCon Plus y Minus.
pip install "shell_motorsport[joycon]"O manualmente:
pip install joycon-python hidapi pyglmimport asyncio
from shell_motorsport import ShellMotorsportCar
from pyjoycon import JoyCon, get_R_id, get_L_id
car_name_plus = "AUTO_PLUS"
car_name_minus = "AUTO_MINUS"
joycon_plus = JoyCon(*get_R_id())
joycon_minus = JoyCon(*get_L_id())
async def main():
car_plus = ShellMotorsportCar()
car_minus = ShellMotorsportCar()
try:
await car_plus.connect_by_name(car_name_plus)
await car_minus.connect_by_name(car_name_minus)
speed_plus = 0x50
speed_minus = 0x50
async def update_status():
while True:
status_plus = joycon_plus.get_status()
status_minus = joycon_minus.get_status()
yield status_plus, status_minus
await asyncio.sleep(0.005)
async for status_plus, status_minus in update_status():
command_plus = car_plus.get_joycon_command(
status_plus, "Plus", speed_plus
)
command_minus = car_minus.get_joycon_command(
status_minus, "Minus", speed_minus
)
# Actualizar velocidad basada en botones
_, _, _, _, speed_plus = car_plus.joycon_handler.parse_joycon_status(
status_plus, "Plus", speed_plus
)
_, _, _, _, speed_minus = car_minus.joycon_handler.parse_joycon_status(
status_minus, "Minus", speed_minus
)
await car_plus.move_command(command_plus)
await car_minus.move_command(command_minus)
finally:
await car_plus.disconnect()
await car_minus.disconnect()
if __name__ == "__main__":
asyncio.run(main())- SR: Avanzar
- SL: Retroceder
- Stick Analógico Y: Girar izquierda/derecha
- A: Velocidad baja (0x16)
- B: Velocidad media (0x32)
- Y: Velocidad alta (0x48)
- X: Velocidad máxima (0x64)
- SR: Avanzar
- SL: Retroceder
- Stick Analógico Y: Girar izquierda/derecha
- ←: Velocidad baja (0x16)
- ↓: Velocidad media (0x32)
- →: Velocidad alta (0x48)
- ↑: Velocidad máxima (0x64)
Puedes seleccionar diferentes niveles de velocidad para tu auto RC. Las velocidades predefinidas son:
- 0x16: Velocidad baja
- 0x32: Velocidad media
- 0x48: Velocidad alta
- 0x64: Velocidad máxima
Puedes usar velocidades personalizadas:
# Usar velocidad personalizada
message = car.retrieve_precomputed_message(forward=1, speed=0x40)
await car.move_command(message)O modificar los perfiles de velocidad en config.py:
DEFAULT_SPEED_PROFILES = {
"low": 0x16,
"medium": 0x32,
"high": 0x48,
"max": 0x64,
}connect(device_id: str) -> None: Conecta a un auto por device_idconnect_by_name(car_name: str) -> None: Conecta a un auto por nombre registradodisconnect() -> None: Desconecta del auto actualmove_command(message: bytes) -> None: Envía un comando de movimientostop() -> None: Detiene el autofind_and_name_car(new_name: str) -> BLEDevice: Busca y registra un nuevo autofind_car(device_id: str) -> BLEDevice: Busca un auto específico
move_forward(speed: int = 0x50, duration: Optional[float] = None) -> Nonemove_backward(speed: int = 0x50, duration: Optional[float] = None) -> None
list_vehicles() -> Dict[str, str]: Lista todos los vehículos registradosget_device_id(car_name: str) -> Optional[str]: Obtiene el device_id de un autois_connected() -> bool: Verifica si está conectadoget_connection_status() -> Dict[str, any]: Obtiene estado de conexiónretrieve_precomputed_message(...) -> bytes: Obtiene mensaje precomputadoget_joycon_command(status: Dict, device_type: str, current_speed: int) -> bytes
Puedes configurar rutas de archivos usando variables de entorno:
export SHELL_MOTORSPORT_VEHICLE_LIST="/path/to/vehicles.json"
export SHELL_MOTORSPORT_COMMANDS_FILE="/path/to/commands.json"vehicle_list.json: Almacena nombres y device_ids de vehículoscar_commands.json: Almacena mensajes precomputados (generado automáticamente)
Para mejorar el rendimiento, puedes precomputar todos los mensajes posibles:
car = ShellMotorsportCar()
car.precompute_messages()Esto generará y guardará todos los mensajes posibles en car_commands.json.
Los autos RC de Shell utilizan Bluetooth Low Energy (BLE) para comunicarse. La librería maneja el protocolo de comunicación que incluye:
- Mensajes cifrados: Los comandos se encriptan utilizando AES-128 en modo ECB.
- Servicios y características BLE: Se utiliza el servicio con UUID
fff0y las características de escritura y notificación correspondientes. - Formato de los mensajes: Los mensajes de control tienen una longitud de 16 bytes y contienen información sobre dirección (avance, retroceso, izquierda, derecha) y velocidad.
Para obtener más detalles técnicos, consulta la siguiente documentación con toda la información de los protocolos y formato de los mensajes.
Para ejecutar los tests:
pip install "shell_motorsport[dev]"
pytest- Asegúrate de que el auto esté encendido y en modo de emparejamiento
- Verifica que Bluetooth esté habilitado en tu sistema
- Intenta aumentar el tiempo de escaneo en
config.py
- Verifica que el auto esté cerca (BLE tiene alcance limitado)
- Asegúrate de que ningún otro dispositivo esté conectado al auto
- Revisa los logs para más detalles
- Verifica que las dependencias de JoyCon estén instaladas
- Asegúrate de que los JoyCon estén emparejados con tu sistema
- Verifica los permisos de acceso USB/HID en tu sistema
Las contribuciones son bienvenidas. Por favor:
- Fork el repositorio
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Este proyecto está bajo la Licencia MIT. Ver el archivo LICENSE para más detalles.
Queremos expresar nuestro agradecimiento a Scrool por sus valiosas contribuciones en la ingeniería inversa, que han sido fundamentales para este proyecto.
- Refactorización completa del código
- Separación en módulos (BLE, encriptación, JoyCon)
- Mejoras en manejo de errores
- Soporte para context managers
- Type hints completos
- Mejoras en logging
- Validación de comandos
- Deadzone para JoyCon
- Mejoras en documentación
- Versión inicial con soporte básico

