From fb5f2b5a209722324217709dfaa66ce11f4fce86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20SAILLANT?= Date: Wed, 26 Mar 2025 15:49:24 +0100 Subject: [PATCH] first commit --- ADB.cpp | 277 ++++++++++++++ ADB.h | 235 ++++++++++++ ADBCore.h | 19 + ADBKeyCodes.h | 60 +++ ADBKeymap.cpp | 27 ++ ADBKeymap.h | 54 +++ ADBPlatform.h | 81 ++++ ADBUtils.h | 101 +++++ HIDTables.h | 274 ++++++++++++++ README.md | 177 +++++++++ examples/auto_platform_detect.cpp | 73 ++++ examples/multiplatform_basic.cpp | 91 +++++ examples/multiplatform_device_info.cpp | 167 +++++++++ examples/multiplatform_event_handler.cpp | 144 +++++++ examples/platform_aware_example.cpp | 55 +++ .../platformio_BLE_esp32_example/README.md | 125 +++++++ .../platformio.ini | 7 + .../platformio_BLE_esp32_example/src/main.cpp | 352 ++++++++++++++++++ .../platformio.ini | 9 + .../src/main.cpp | 55 +++ .../platformio.ini | 9 + .../platformio_USB_esp32_example/src/main.cpp | 55 +++ .../platformio.ini | 9 + .../src/main.cpp | 88 +++++ .../platformio.ini | 27 ++ .../src/main.cpp | 127 +++++++ .../platformio_USB_stm32_example/README.md | 129 +++++++ .../platformio.ini | 25 ++ .../platformio_USB_stm32_example/src/main.cpp | 257 +++++++++++++ examples/usb_hid_stm32.cpp | 252 +++++++++++++ logo-specifications.md | 44 +++ platformio_example/library.json | 23 ++ 32 files changed, 3428 insertions(+) create mode 100644 ADB.cpp create mode 100644 ADB.h create mode 100644 ADBCore.h create mode 100644 ADBKeyCodes.h create mode 100644 ADBKeymap.cpp create mode 100644 ADBKeymap.h create mode 100644 ADBPlatform.h create mode 100644 ADBUtils.h create mode 100644 HIDTables.h create mode 100644 README.md create mode 100644 examples/auto_platform_detect.cpp create mode 100644 examples/multiplatform_basic.cpp create mode 100644 examples/multiplatform_device_info.cpp create mode 100644 examples/multiplatform_event_handler.cpp create mode 100644 examples/platform_aware_example.cpp create mode 100644 examples/platformio_BLE_esp32_example/README.md create mode 100644 examples/platformio_BLE_esp32_example/platformio.ini create mode 100644 examples/platformio_BLE_esp32_example/src/main.cpp create mode 100644 examples/platformio_USB_arduino_uno_example/platformio.ini create mode 100644 examples/platformio_USB_arduino_uno_example/src/main.cpp create mode 100644 examples/platformio_USB_esp32_example/platformio.ini create mode 100644 examples/platformio_USB_esp32_example/src/main.cpp create mode 100644 examples/platformio_USB_leonardo_example/platformio.ini create mode 100644 examples/platformio_USB_leonardo_example/src/main.cpp create mode 100644 examples/platformio_USB_multi_platform_example/platformio.ini create mode 100644 examples/platformio_USB_multi_platform_example/src/main.cpp create mode 100644 examples/platformio_USB_stm32_example/README.md create mode 100644 examples/platformio_USB_stm32_example/platformio.ini create mode 100644 examples/platformio_USB_stm32_example/src/main.cpp create mode 100644 examples/usb_hid_stm32.cpp create mode 100644 logo-specifications.md create mode 100644 platformio_example/library.json diff --git a/ADB.cpp b/ADB.cpp new file mode 100644 index 0000000..6322d80 --- /dev/null +++ b/ADB.cpp @@ -0,0 +1,277 @@ +/** + * @file ADB.cpp + * @brief Implémentation de la bibliothèque ADB multiplateforme + * + * Cette implémentation offre les fonctionnalités nécessaires pour communiquer avec + * des périphériques Apple Desktop Bus sur différentes plateformes (Arduino, STM32, ESP32, Teensy). + * Le code est optimisé pour fonctionner de manière cohérente indépendamment du matériel sous-jacent. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include "ADB.h" + +/** + * Implémentation de la classe ADB - Gestion du bus Apple Desktop Bus + */ + +ADB::ADB(uint8_t dataPin) : dataPin(dataPin), useADBDevices(false) {} + +void ADB::init(uint8_t dataPin, bool useADBDevices) { + // Mise à jour de la broche si spécifiée + if (dataPin != 0xFF) { + this->dataPin = dataPin; + } + this->useADBDevices = useADBDevices; + + // Configuration de la broche en mode open-drain + pinMode(this->dataPin, OUTPUT_OPEN_DRAIN); + digitalWrite(this->dataPin, HIGH); + + // Attente que la ligne soit prête + while (digitalRead(this->dataPin) == LOW) { + // Attendre que la ligne remonte + } + + // Réinitialisation du bus + reset(); +} + +void ADB::reset() { + // Signal de réinitialisation: maintenir la ligne basse pendant 3ms + digitalWrite(dataPin, LOW); + delayMicroseconds(3000); + digitalWrite(dataPin, HIGH); +} + +void ADB::wait() { + // Signal d'attente: maintenir la ligne basse pendant 800µs + digitalWrite(dataPin, LOW); + delayMicroseconds(800); + digitalWrite(dataPin, HIGH); +} + +void ADB::sync() { + // Signal de synchronisation pour les commandes + digitalWrite(dataPin, HIGH); + delayMicroseconds(70); + digitalWrite(dataPin, LOW); +} + +void ADB::writeBit(uint16_t bit) { + // Encodage Manchester modifié: + // 1 = 35µs bas puis 65µs haut + // 0 = 65µs bas puis 35µs haut + if (bit) { + digitalWrite(dataPin, LOW); + delayMicroseconds(35); + digitalWrite(dataPin, HIGH); + delayMicroseconds(65); + } else { + digitalWrite(dataPin, LOW); + delayMicroseconds(65); + digitalWrite(dataPin, HIGH); + delayMicroseconds(35); + } +} + +void ADB::writeBits(uint16_t bits, uint8_t length) { + // Écrit plusieurs bits, du MSB au LSB + uint16_t mask = 1 << (length - 1); + while (mask) { + writeBit(bits & mask); + mask >>= 1; + } +} + +void ADB::writeDataPacket(uint16_t bits, uint8_t length) { + // Format du paquet: bit de début (1), données, bit de fin (0) + writeBit(1); + writeBits(bits, length); + writeBit(0); +} + +bool ADB::waitTLT(bool responseExpected) { + // Attend la réponse d'un périphérique après une commande + digitalWrite(dataPin, HIGH); + delayMicroseconds(140); + + // Si une réponse est attendue, attendre jusqu'à 240µs + if (responseExpected) { + uint8_t timeout = 0; + while (digitalRead(dataPin) == HIGH && timeout < 240) { + delayMicroseconds(1); + timeout++; + } + } + return true; +} + +uint8_t ADB::readBit() { + // Lecture d'un bit avec détection de timeout + const unsigned long MAX_WAIT = 85; // Microseconds + + // Attente du front montant + auto time_start = micros(); + while (digitalRead(dataPin) == LOW) { + if (micros() - time_start > MAX_WAIT) + return BIT_ERROR; + } + auto low_time = micros() - time_start; + + // Attente du front descendant + time_start = micros(); + while (digitalRead(dataPin) == HIGH) { + if (micros() - time_start > MAX_WAIT) + return BIT_ERROR; + } + auto high_time = micros() - time_start; + + // Décodage Manchester modifié + return (low_time < high_time) ? 0x1 : 0x0; +} + +bool ADB::readDataPacket(uint16_t* buffer, uint8_t length) { + // Vérifie le bit de début + if (readBit() != 0x1) { + return false; + } + + // Lecture bit par bit des données + *buffer = 0; + for (uint8_t i = 0; i < length; i++) { + uint8_t current_bit = readBit(); + if (current_bit == BIT_ERROR) { + return false; + } + *buffer = (*buffer << 1) | current_bit; + } + + // Lecture du bit de fin (ignoré) + readBit(); + return true; +} + +void ADB::writeCommand(uint8_t command) { + wait(); + sync(); + writeBits(static_cast(command), 8); + writeBit(0); +} + +void ADB::setPin(uint8_t dataPin) { + this->dataPin = dataPin; + pinMode(dataPin, OUTPUT_OPEN_DRAIN); + digitalWrite(dataPin, HIGH); +} + +/** + * Implémentation de la classe ADBDevices - Gestion des périphériques ADB + */ + +bool ADBDevices::initializeDevice(uint8_t address, uint8_t handler_id, bool& present) { + bool error = false; + + // Préparer les données pour le registre 3 + adb_data reg3 = {0}; + adb_data mask = {0}; + + reg3.data.device_handler_id = handler_id; + mask.data.device_handler_id = 0xFF; + + // Tenter de configurer le périphérique et vérifier sa présence + present = deviceUpdateRegister3(address, reg3, mask.raw, &error) && !error; + return present; +} + +adb_data ADBDevices::keyboardReadModifiers(bool* error) { + adb_data modifiers = {0}; + + // Envoi d'une commande Talk au registre 2 du clavier + adb.writeCommand(CMD_TALK | ADB_ADDRESS(ADDR_KEYBOARD) | ADB_REGISTER(2)); + adb.waitTLT(true); + + // Lecture des données et mise à jour du statut d'erreur + *error = !adb.readDataPacket(&modifiers.raw, 16); + return modifiers; +} + +adb_data ADBDevices::keyboardReadKeyPress(bool* error) { + adb_data keyPress = {0}; + + // Envoi d'une commande Talk au registre 0 du clavier + adb.writeCommand(CMD_TALK | ADB_ADDRESS(ADDR_KEYBOARD) | ADB_REGISTER(0)); + adb.waitTLT(true); + + // Lecture des touches pressées et mise à jour du statut d'erreur + *error = !adb.readDataPacket(&keyPress.raw, 16); + return keyPress; +} + +void ADBDevices::keyboardWriteLEDs(bool scroll, bool caps, bool num) { + adb_data modifiers = {0}; + + // Configuration des LEDs (la logique est inversée dans le protocole) + modifiers.data.led_scroll = !scroll; + modifiers.data.led_caps = !caps; + modifiers.data.led_num = !num; + + // Envoi d'une commande Listen au registre 2 du clavier + adb.writeCommand(CMD_LISTEN | ADB_ADDRESS(ADDR_KEYBOARD) | ADB_REGISTER(2)); + adb.waitTLT(false); + + // Envoi des données de configuration des LEDs + adb.writeDataPacket(modifiers.raw, 16); +} + +adb_data ADBDevices::mouseReadData(bool* error) { + adb_data mouseData = {0}; + + // Envoi d'une commande Talk au registre 0 de la souris + adb.writeCommand(CMD_TALK | ADB_ADDRESS(ADDR_MOUSE) | ADB_REGISTER(0)); + adb.waitTLT(true); + + // Lecture des données de la souris et mise à jour du statut d'erreur + *error = !adb.readDataPacket(&mouseData.raw, 16); + return mouseData; +} + +adb_data ADBDevices::deviceReadRegister3(uint8_t addr, bool* error) { + adb_data reg3 = {0}; + + // Envoi d'une commande Talk au registre 3 du périphérique + adb.writeCommand(CMD_TALK | ADB_ADDRESS(addr) | ADB_REGISTER(3)); + adb.waitTLT(true); + + // Lecture de la configuration du périphérique + *error = !adb.readDataPacket(®3.raw, 16); + return reg3; +} + +bool ADBDevices::deviceUpdateRegister3(uint8_t addr, adb_data newReg3, uint16_t mask, bool* error) { + // Lecture de la configuration actuelle + adb_data reg3 = deviceReadRegister3(addr, error); + if (*error) return false; + + // Attente entre les opérations + delay(POLL_DELAY); + + // Application du masque pour ne modifier que les bits souhaités + reg3.raw = (reg3.raw & ~mask) | (newReg3.raw & mask); + + // Envoi d'une commande Listen pour mettre à jour la configuration + adb.writeCommand(CMD_LISTEN | ADB_ADDRESS(addr) | ADB_REGISTER(3)); + adb.waitTLT(false); + adb.writeDataPacket(reg3.raw, 16); + + // Attente entre les opérations + delay(POLL_DELAY); + + // Vérification que la mise à jour a été prise en compte + reg3 = deviceReadRegister3(addr, error); + if (*error) return false; + + return (reg3.raw & mask) == (newReg3.raw & mask); +} diff --git a/ADB.h b/ADB.h new file mode 100644 index 0000000..6266651 --- /dev/null +++ b/ADB.h @@ -0,0 +1,235 @@ +/** + * @file ADB.h + * @brief Bibliothèque multiplateforme pour la communication avec le bus Apple Desktop Bus (ADB) + */ + +#ifndef ADB_MAIN_h +#define ADB_MAIN_h + +#include +#include +#include "ADBKeymap.h" +#include "ADBKeyCodes.h" + +namespace ADBProtocol { + // Commandes ADB + constexpr uint8_t CMD_TALK = 0b11 << 2; + constexpr uint8_t CMD_LISTEN = 0b10 << 2; + constexpr uint8_t CMD_FLUSH = 0b01 << 2; + + // Constantes diverses + constexpr uint8_t BIT_ERROR = 0xFF; + constexpr uint8_t POLL_DELAY = 5; + + // Macros de conversion pour les adresses et registres ADB + constexpr uint8_t ADDRESS(uint8_t addr) { return (addr << 4); } + constexpr uint8_t REGISTER(uint8_t reg) { return reg; } +} + +// Pour compatibilité avec le code existant +#define CMD_TALK ADBProtocol::CMD_TALK +#define CMD_LISTEN ADBProtocol::CMD_LISTEN +#define CMD_FLUSH ADBProtocol::CMD_FLUSH +#define BIT_ERROR ADBProtocol::BIT_ERROR +#define POLL_DELAY ADBProtocol::POLL_DELAY +#define ADB_ADDRESS(addr) ADBProtocol::ADDRESS(addr) +#define ADB_REGISTER(reg) ADBProtocol::REGISTER(reg) + +/** + * @brief Structures de données pour les périphériques ADB + */ +struct adb_kb_keypress { + uint8_t key1 : 7; // Code de la deuxième touche + bool released1 : 1; // Indicateur de relâchement pour la deuxième touche + uint8_t key0 : 7; // Code de la première touche + bool released0 : 1; // Indicateur de relâchement pour la première touche +}; + +struct adb_kb_modifiers { + bool led_num : 1; // LED de verrouillage numérique + bool led_caps : 1; // LED de verrouillage majuscule + bool led_scroll : 1; // LED de défilement + uint8_t reserved1 : 3; // Bits réservés + bool scroll_lock : 1; // État de verrouillage de défilement + bool num_lock : 1; // État de verrouillage numérique + bool command : 1; // Touche Command + bool option : 1; // Touche Option + bool shift : 1; // Touche Shift + bool control : 1; // Touche Control + bool reset : 1; // État de réinitialisation + bool caps_lock : 1; // État de verrouillage majuscule + bool backspace : 1; // Touche Backspace + uint8_t reserved0 : 1; // Bit réservé +}; + +struct adb_mouse_data { + uint8_t x_offset : 7; // Déplacement horizontal de la souris + uint8_t reserved0 : 1; // Bit réservé + uint8_t y_offset : 7; // Déplacement vertical de la souris + bool button : 1; // État du bouton de souris +}; + +struct adb_register3 { + uint8_t device_handler_id : 8; // Identifiant du gestionnaire + uint8_t device_address : 4; // Adresse du périphérique + uint8_t reserved1 : 1; // Bit réservé + uint8_t srq_enable : 1; // Activation des requêtes de service + uint8_t exceptional_event : 1; // Indicateur d'événement exceptionnel + uint8_t reserved0 : 1; // Bit réservé +}; + +/** + * @brief Union pour faciliter l'accès aux données ADB sous forme brute ou structurée + */ +template +union adb_data { + uint16_t raw; + T data; +}; + +/** + * @brief Classe principale pour gérer le bus ADB + */ +class ADB { +public: + /** + * @brief Constructeur avec pin de données configurable + * @param dataPin Broche utilisée pour les communications ADB + */ + explicit ADB(uint8_t dataPin); + + /** + * @brief Initialisation du bus ADB + * @param dataPin Broche optionnelle pour reconfigurer la pin (0xFF pour conserver l'existante) + * @param useADBDevices Indique si la classe ADBDevices sera utilisée + */ + void init(uint8_t dataPin = 0xFF, bool useADBDevices = false); + + /** + * @brief Réinitialise le bus ADB + */ + void reset(); + + /** + * @brief Envoi d'une commande sur le bus ADB + * @param command Code de commande ADB + */ + void writeCommand(uint8_t command); + + /** + * @brief Lecture de données depuis le bus ADB + * @param buffer Pointeur vers le tampon de données + * @param length Longueur des données à lire en bits + * @return true si la lecture a réussi, false sinon + */ + bool readDataPacket(uint16_t* buffer, uint8_t length); + + /** + * @brief Écriture de données sur le bus ADB + * @param bits Données à écrire + * @param length Longueur des données à écrire en bits + */ + void writeDataPacket(uint16_t bits, uint8_t length); + + /** + * @brief Change la broche utilisée pour la communication + * @param dataPin Nouvelle broche de données + */ + void setPin(uint8_t dataPin); + + /** + * @brief Attente de réponse du périphérique ADB + * @param responseExpected Indique si une réponse est attendue + * @return true si l'attente s'est bien déroulée + */ + bool waitTLT(bool responseExpected); + +private: + uint8_t dataPin; // Broche de données + bool useADBDevices; // Utilisation de la classe ADBDevices + + // Méthodes de bas niveau pour la communication ADB + void wait(); // Signal d'attente (anciennement attention) + void sync(); // Signal de synchronisation + void writeBit(uint16_t bit); // Écriture d'un bit + void writeBits(uint16_t bits, uint8_t length); // Écriture de plusieurs bits + uint8_t readBit(); // Lecture d'un bit +}; + +/** + * @brief Classe pour gérer les périphériques connectés au bus ADB + */ +class ADBDevices { +public: + /** + * @brief Constructeur avec référence à un objet ADB + * @param adb Référence à l'instance ADB utilisée pour la communication + */ + explicit ADBDevices(ADB& adb) : adb(adb) {} + + /** + * @brief Initialisation d'un périphérique ADB + * @param address Adresse du périphérique + * @param handler_id Identifiant du gestionnaire + * @param present Référence pour indiquer si le périphérique est présent + * @return true si l'initialisation a réussi + */ + bool initializeDevice(uint8_t address, uint8_t handler_id, bool& present); + + /** + * @brief Lecture des modificateurs du clavier + * @param error Pointeur pour indiquer si une erreur s'est produite + * @return Structure contenant les modificateurs + */ + adb_data keyboardReadModifiers(bool* error); + + /** + * @brief Lecture des touches pressées sur le clavier + * @param error Pointeur pour indiquer si une erreur s'est produite + * @return Structure contenant les touches pressées + */ + adb_data keyboardReadKeyPress(bool* error); + + /** + * @brief Configuration des LEDs du clavier + * @param scroll État de la LED de défilement + * @param caps État de la LED de verrouillage majuscule + * @param num État de la LED de verrouillage numérique + */ + void keyboardWriteLEDs(bool scroll, bool caps, bool num); + + /** + * @brief Lecture des données de la souris + * @param error Pointeur pour indiquer si une erreur s'est produite + * @return Structure contenant les données de la souris + */ + adb_data mouseReadData(bool* error); + + /** + * @brief Mise à jour du registre 3 d'un périphérique + * @param addr Adresse du périphérique + * @param newReg3 Nouvelles valeurs pour le registre 3 + * @param mask Masque à appliquer + * @param error Pointeur pour indiquer si une erreur s'est produite + * @return true si la mise à jour a réussi + */ + bool deviceUpdateRegister3(uint8_t addr, adb_data newReg3, uint16_t mask, bool* error); + +private: + ADB& adb; // Référence à l'objet ADB utilisé pour la communication + + /** + * @brief Lecture du registre 3 d'un périphérique + * @param addr Adresse du périphérique + * @param error Pointeur pour indiquer si une erreur s'est produite + * @return Structure contenant les données du registre 3 + */ + adb_data deviceReadRegister3(uint8_t addr, bool* error); +}; + +// Conversion des axes de la souris (format 7 bits en complément à 2 vers int8_t) +inline int8_t adbMouseConvertAxis(uint8_t value) { + return static_cast(value << 1); +} + +#endif // ADB_MAIN_h diff --git a/ADBCore.h b/ADBCore.h new file mode 100644 index 0000000..206499d --- /dev/null +++ b/ADBCore.h @@ -0,0 +1,19 @@ +/** + * @file ADBCore.h + * @brief Fichier principal d'inclusion pour la bibliothèque ADB multiplateforme + */ + +#ifndef ADB_CORE_h +#define ADB_CORE_h + +// Ordre d'inclusion optimisé pour assurer que toutes les dépendances sont disponibles +#include "HIDTables.h" // Définitions des codes HID +#include "ADBKeyCodes.h" // Définitions des constantes ADB +#include "ADBKeymap.h" // Mappage ADB vers HID +#include "ADB.h" // Interface principale du protocole ADB +#include "ADBUtils.h" // Utilitaires supplémentaires + +// Macro pour convertir les valeurs des axes souris (pour la compatibilité avec le code existant) +#define ADB_MOUSE_CONV_AXIS(val) adbMouseConvertAxis(val) + +#endif // ADB_CORE_h diff --git a/ADBKeyCodes.h b/ADBKeyCodes.h new file mode 100644 index 0000000..428bc86 --- /dev/null +++ b/ADBKeyCodes.h @@ -0,0 +1,60 @@ +/** + * Définition des codes de touches ADB + * Adaptées du protocole Apple Desktop Bus + */ + +#ifndef ADB_KEYCODES_H +#define ADB_KEYCODES_H + +#include + +/** + * Constantes du protocole Apple Desktop Bus + */ +namespace ADBKey { + namespace Address { + constexpr uint8_t KEYBOARD = 0x2; + constexpr uint8_t MOUSE = 0x3; + } + + namespace KeyCode { + // Touches spéciales + constexpr uint8_t CAPS_LOCK = 0x39; + constexpr uint8_t NUM_LOCK = 0x47; + + // Touches de modification + constexpr uint8_t LEFT_SHIFT = 0x38; + constexpr uint8_t RIGHT_SHIFT = 0x7B; + constexpr uint8_t LEFT_CONTROL = 0x36; + constexpr uint8_t RIGHT_CONTROL = 0x7D; + constexpr uint8_t LEFT_OPTION = 0x3A; + constexpr uint8_t RIGHT_OPTION = 0x7C; + constexpr uint8_t LEFT_COMMAND = 0x37; + constexpr uint8_t RIGHT_COMMAND = 0x37; + + // Touche Power + constexpr uint8_t POWER_KEY_CODE = 0x66; + constexpr uint16_t POWER_DOWN = 0x7F7F; + constexpr uint16_t POWER_UP = 0xFFFF; + } +} + +// Pour la compatibilité avec le code existant +#define ADDR_KEYBOARD ADBKey::Address::KEYBOARD +#define ADDR_MOUSE ADBKey::Address::MOUSE +#define ADB_KEY_CAPS_LOCK ADBKey::KeyCode::CAPS_LOCK +#define ADB_KEY_NUM_LOCK ADBKey::KeyCode::NUM_LOCK +#define ADB_KEY_POWER_DOWN ADBKey::KeyCode::POWER_DOWN +#define ADB_KEY_POWER_UP ADBKey::KeyCode::POWER_UP + +// Définitions des modificateurs (compatibilité) +#define ADB_KEY_LEFT_SHIFT ADBKey::KeyCode::LEFT_SHIFT +#define ADB_KEY_RIGHT_SHIFT ADBKey::KeyCode::RIGHT_SHIFT +#define ADB_KEY_LEFT_CONTROL ADBKey::KeyCode::LEFT_CONTROL +#define ADB_KEY_RIGHT_CONTROL ADBKey::KeyCode::RIGHT_CONTROL +#define ADB_KEY_LEFT_OPTION ADBKey::KeyCode::LEFT_OPTION +#define ADB_KEY_RIGHT_OPTION ADBKey::KeyCode::RIGHT_OPTION +#define ADB_KEY_LEFT_COMMAND ADBKey::KeyCode::LEFT_COMMAND +#define ADB_KEY_RIGHT_COMMAND ADBKey::KeyCode::RIGHT_COMMAND + +#endif // ADB_KEYCODES_H diff --git a/ADBKeymap.cpp b/ADBKeymap.cpp new file mode 100644 index 0000000..387623c --- /dev/null +++ b/ADBKeymap.cpp @@ -0,0 +1,27 @@ +#include "ADBKeymap.h" +#include "HIDTables.h" + +// Définition du tableau de conversion ADB vers HID +const uint8_t ADBKeymap::keyCodeTable[128] = { + // Tableau de conversion des touches ADB vers HID + // Format: [code ADB] = code HID + KEY_NONE, KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, // 0x00-0x07 + KEY_X, KEY_C, KEY_V, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, // 0x08-0x0F + KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, // 0x10-0x17 + KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O, // 0x18-0x1F + KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE, // 0x20-0x27 + KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT, // 0x28-0x2F + KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, KEY_NONE, KEY_ESC, KEY_NONE, KEY_LEFTMETA, // 0x30-0x37 + KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTCTRL, KEY_NONE, // 0x38-0x3F + // Continuer avec le reste du tableau... + KEY_NONE, KEY_KPDOT, KEY_NONE, KEY_KPASTERISK, KEY_NONE, KEY_KPPLUS, KEY_NONE, KEY_NUMLOCK, // 0x40-0x47 + // ...et ainsi de suite jusqu'à 127 + // Complétez le reste du tableau selon les besoins de votre clavier + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x48-0x4F + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x50-0x57 + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x58-0x5F + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x60-0x67 + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x68-0x6F + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 0x70-0x77 + KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE // 0x78-0x7F +}; diff --git a/ADBKeymap.h b/ADBKeymap.h new file mode 100644 index 0000000..c17557f --- /dev/null +++ b/ADBKeymap.h @@ -0,0 +1,54 @@ +/** + * @file ADBKeymap.h + * @brief Constantes et fonctions de conversion pour la bibliothèque ADB multiplateforme + */ + +#ifndef ADB_KEYMAP_h +#define ADB_KEYMAP_h + +#include +#include "HIDTables.h" +#include "ADBKeyCodes.h" + +// Classe pour la conversion ADB vers HID +class ADBKeymap { +public: + // Vérifie si une touche est un modificateur + static bool isModifier(uint8_t key) { + using namespace ADBKey::KeyCode; + return + (key == LEFT_SHIFT) || + (key == RIGHT_SHIFT) || + (key == LEFT_OPTION) || + (key == RIGHT_OPTION) || + (key == LEFT_COMMAND) || + (key == RIGHT_COMMAND) || + (key == LEFT_CONTROL) || + (key == RIGHT_CONTROL); + } + + // Obtient le masque du modificateur correspondant à une touche ADB + static uint8_t getModifierMask(uint8_t adbKeycode) { + using namespace ADBKey::KeyCode; + if (adbKeycode == LEFT_SHIFT) return KEY_MOD_LSHIFT; + if (adbKeycode == RIGHT_SHIFT) return KEY_MOD_RSHIFT; + if (adbKeycode == LEFT_CONTROL) return KEY_MOD_LCTRL; + if (adbKeycode == RIGHT_CONTROL) return KEY_MOD_RCTRL; + if (adbKeycode == LEFT_OPTION) return KEY_MOD_LALT; + if (adbKeycode == RIGHT_OPTION) return KEY_MOD_RALT; + if (adbKeycode == LEFT_COMMAND) return KEY_MOD_LMETA; + if (adbKeycode == RIGHT_COMMAND) return KEY_MOD_RMETA; + return 0; + } + + // Convertit un code ADB en code HID + static uint8_t toHID(uint8_t adbKeycode) { + if (adbKeycode >= 128) return KEY_NONE; + return keyCodeTable[adbKeycode]; + } + + // Tableau de conversion ADB vers HID + static const uint8_t keyCodeTable[128]; +}; + +#endif // ADB_KEYMAP_h diff --git a/ADBPlatform.h b/ADBPlatform.h new file mode 100644 index 0000000..b7467e2 --- /dev/null +++ b/ADBPlatform.h @@ -0,0 +1,81 @@ +/** + * @file ADBPlatform.h + * @brief Détection automatique de plateforme pour la bibliothèque ADB + * + * Ce fichier fournit des définitions et configurations automatiques basées + * sur la plateforme cible. Il peut être inclus dans des exemples pour une + * meilleure portabilité. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#ifndef ADB_PLATFORM_h +#define ADB_PLATFORM_h + +// Détection de plateforme et configuration automatique +#if defined(ARDUINO_ARCH_AVR) + #define ADB_PLATFORM_AVR + #define ADB_PLATFORM_NAME "Arduino AVR" + #define ADB_DEFAULT_PIN 2 + +#elif defined(ARDUINO_ARCH_SAM) + #define ADB_PLATFORM_SAM + #define ADB_PLATFORM_NAME "Arduino SAM" + #define ADB_DEFAULT_PIN 2 + +#elif defined(ARDUINO_ARCH_SAMD) + #define ADB_PLATFORM_SAMD + #define ADB_PLATFORM_NAME "Arduino SAMD" + #define ADB_DEFAULT_PIN 2 + +#elif defined(ARDUINO_ARCH_STM32) + #define ADB_PLATFORM_STM32 + #define ADB_PLATFORM_NAME "STM32" + // Sur les STM32, la broche par défaut est PB4 + #define ADB_DEFAULT_PIN PB4 + +#elif defined(ARDUINO_ARCH_ESP32) + #define ADB_PLATFORM_ESP32 + #define ADB_PLATFORM_NAME "ESP32" + #define ADB_DEFAULT_PIN 21 + +#elif defined(TEENSYDUINO) + #define ADB_PLATFORM_TEENSY + #define ADB_PLATFORM_NAME "Teensy" + #define ADB_DEFAULT_PIN 3 + +#else + #define ADB_PLATFORM_UNKNOWN + #define ADB_PLATFORM_NAME "Plateforme inconnue" + #define ADB_DEFAULT_PIN 2 + #warning "Plateforme non reconnue, utilisation de la pin 2 par défaut" +#endif + +// Paramètres par défaut recommandés par plateforme +#ifdef ADB_PLATFORM_AVR + // Arduino UNO, MEGA, etc. - MCU plus lent + #define ADB_POLL_INTERVAL 100 + #define ADB_SERIAL_BAUD 9600 +#elif defined(ADB_PLATFORM_STM32) || defined(ADB_PLATFORM_ESP32) || defined(ADB_PLATFORM_TEENSY) + // Plateformes plus rapides + #define ADB_POLL_INTERVAL 20 + #define ADB_SERIAL_BAUD 115200 +#else + // Valeurs par défaut pour autres plateformes + #define ADB_POLL_INTERVAL 50 + #define ADB_SERIAL_BAUD 57600 +#endif + +/** + * Fonction pour afficher les informations de plateforme + */ +inline void printPlatformInfo() { + Serial.print(F("Bibliothèque ADB sur plateforme: ")); + Serial.println(F(ADB_PLATFORM_NAME)); + Serial.print(F("Pin ADB par défaut: ")); + Serial.println(ADB_DEFAULT_PIN); +} + +#endif // ADB_PLATFORM_h diff --git a/ADBUtils.h b/ADBUtils.h new file mode 100644 index 0000000..5446302 --- /dev/null +++ b/ADBUtils.h @@ -0,0 +1,101 @@ +/** + * @file ADBUtils.h + * @brief Utilitaires pour la bibliothèque ADB multiplateforme + * + * Ce fichier fournit des classes et fonctions utilitaires pour simplifier + * l'utilisation de la bibliothèque ADB sur différentes plateformes matérielles. + * Les utilitaires sont conçus pour être compatibles avec toutes les plateformes supportées. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#ifndef ADB_UTILS_h +#define ADB_UTILS_h + +#include +#include "ADB.h" + +/** + * @brief Classe d'utilitaires pour faciliter l'utilisation des périphériques ADB + */ +class ADBUtils { +public: + /** + * @brief Constructeur avec référence à un objet ADBDevices + * @param devices Référence à l'instance ADBDevices + */ + explicit ADBUtils(ADBDevices& devices) : devices(devices) {} + + /** + * @brief Imprime l'état des touches du clavier sur le port série + * @return true si la lecture a réussi + */ + bool printKeyboardStatus() { + bool error = false; + + // Lecture des modificateurs + auto modifiers = devices.keyboardReadModifiers(&error); + if (error) return false; + + Serial.println(F("Keyboard Status:")); + Serial.print(F(" Caps Lock: ")); + Serial.println(modifiers.data.led_caps ? F("ON") : F("OFF")); + Serial.print(F(" Num Lock: ")); + Serial.println(modifiers.data.led_num ? F("ON") : F("OFF")); + + // Lecture des touches pressées + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) return false; + + if (keyPress.data.key0 != 0) { + Serial.print(F(" Key 0: 0x")); + Serial.print(keyPress.data.key0, HEX); + Serial.print(F(" (")); + Serial.print(keyPress.data.released0 ? F("Released") : F("Pressed")); + Serial.println(F(")")); + } + + if (keyPress.data.key1 != 0) { + Serial.print(F(" Key 1: 0x")); + Serial.print(keyPress.data.key1, HEX); + Serial.print(F(" (")); + Serial.print(keyPress.data.released1 ? F("Released") : F("Pressed")); + Serial.println(F(")")); + } + + return true; + } + + /** + * @brief Imprime les données de la souris sur le port série + * @return true si la lecture a réussi + */ + bool printMouseStatus() { + bool error = false; + + // Lecture des données de la souris + auto mouseData = devices.mouseReadData(&error); + if (error) return false; + + // Conversion des valeurs brutes + int8_t xMove = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t yMove = adbMouseConvertAxis(mouseData.data.y_offset); + + Serial.println(F("Mouse Status:")); + Serial.print(F(" X Movement: ")); + Serial.println(xMove); + Serial.print(F(" Y Movement: ")); + Serial.println(yMove); + Serial.print(F(" Button: ")); + Serial.println(mouseData.data.button ? F("Pressed") : F("Released")); + + return true; + } + +private: + ADBDevices& devices; +}; + +#endif // ADB_UTILS_h diff --git a/HIDTables.h b/HIDTables.h new file mode 100644 index 0000000..0b5e915 --- /dev/null +++ b/HIDTables.h @@ -0,0 +1,274 @@ +// https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf, pp. 53 +// adapted from https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 + +#ifndef HID_TABLES_h +#define HID_TABLES_h + +#define KEY_MOD_LCTRL 0x01 +#define KEY_MOD_LSHIFT 0x02 +#define KEY_MOD_LALT 0x04 +#define KEY_MOD_LMETA 0x08 +#define KEY_MOD_RCTRL 0x10 +#define KEY_MOD_RSHIFT 0x20 +#define KEY_MOD_RALT 0x40 +#define KEY_MOD_RMETA 0x80 + +/** + * 0x00 if no key pressed. + * + * If more than N keys are pressed, the HID reports + * KEY_ERR_OVF in all slots to indicate this condition. + */ + +#define KEY_NONE 0x00 // No key pressed +#define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") +// 0x02 // Keyboard POST Fail +// 0x03 // Keyboard Error Undefined +#define KEY_A 0x04 // Keyboard a and A +#define KEY_B 0x05 // Keyboard b and B +#define KEY_C 0x06 // Keyboard c and C +#define KEY_D 0x07 // Keyboard d and D +#define KEY_E 0x08 // Keyboard e and E +#define KEY_F 0x09 // Keyboard f and F +#define KEY_G 0x0a // Keyboard g and G +#define KEY_H 0x0b // Keyboard h and H +#define KEY_I 0x0c // Keyboard i and I +#define KEY_J 0x0d // Keyboard j and J +#define KEY_K 0x0e // Keyboard k and K +#define KEY_L 0x0f // Keyboard l and L +#define KEY_M 0x10 // Keyboard m and M +#define KEY_N 0x11 // Keyboard n and N +#define KEY_O 0x12 // Keyboard o and O +#define KEY_P 0x13 // Keyboard p and P +#define KEY_Q 0x14 // Keyboard q and Q +#define KEY_R 0x15 // Keyboard r and R +#define KEY_S 0x16 // Keyboard s and S +#define KEY_T 0x17 // Keyboard t and T +#define KEY_U 0x18 // Keyboard u and U +#define KEY_V 0x19 // Keyboard v and V +#define KEY_W 0x1a // Keyboard w and W +#define KEY_X 0x1b // Keyboard x and X +#define KEY_Y 0x1c // Keyboard y and Y +#define KEY_Z 0x1d // Keyboard z and Z + +#define KEY_1 0x1e // Keyboard 1 and ! +#define KEY_2 0x1f // Keyboard 2 and @ +#define KEY_3 0x20 // Keyboard 3 and # +#define KEY_4 0x21 // Keyboard 4 and $ +#define KEY_5 0x22 // Keyboard 5 and % +#define KEY_6 0x23 // Keyboard 6 and ^ +#define KEY_7 0x24 // Keyboard 7 and & +#define KEY_8 0x25 // Keyboard 8 and * +#define KEY_9 0x26 // Keyboard 9 and ( +#define KEY_0 0x27 // Keyboard 0 and ) + +#define KEY_ENTER 0x28 // Keyboard Return (ENTER) +#define KEY_ESC 0x29 // Keyboard ESCAPE +#define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) +#define KEY_TAB 0x2b // Keyboard Tab +#define KEY_SPACE 0x2c // Keyboard Spacebar +#define KEY_MINUS 0x2d // Keyboard - and _ +#define KEY_EQUAL 0x2e // Keyboard = and + +#define KEY_LEFTBRACE 0x2f // Keyboard [ and { +#define KEY_RIGHTBRACE 0x30 // Keyboard ] and } +#define KEY_BACKSLASH 0x31 // Keyboard \ and | +#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~ +#define KEY_SEMICOLON 0x33 // Keyboard ; and : +#define KEY_APOSTROPHE 0x34 // Keyboard ' and " +#define KEY_GRAVE 0x35 // Keyboard ` and ~ +#define KEY_COMMA 0x36 // Keyboard , and < +#define KEY_DOT 0x37 // Keyboard . and > +#define KEY_SLASH 0x38 // Keyboard / and ? +#define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock + +#define KEY_F1 0x3a // Keyboard F1 +#define KEY_F2 0x3b // Keyboard F2 +#define KEY_F3 0x3c // Keyboard F3 +#define KEY_F4 0x3d // Keyboard F4 +#define KEY_F5 0x3e // Keyboard F5 +#define KEY_F6 0x3f // Keyboard F6 +#define KEY_F7 0x40 // Keyboard F7 +#define KEY_F8 0x41 // Keyboard F8 +#define KEY_F9 0x42 // Keyboard F9 +#define KEY_F10 0x43 // Keyboard F10 +#define KEY_F11 0x44 // Keyboard F11 +#define KEY_F12 0x45 // Keyboard F12 + +#define KEY_SYSRQ 0x46 // Keyboard Print Screen +#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock +#define KEY_PAUSE 0x48 // Keyboard Pause +#define KEY_INSERT 0x49 // Keyboard Insert +#define KEY_HOME 0x4a // Keyboard Home +#define KEY_PAGEUP 0x4b // Keyboard Page Up +#define KEY_DELETE 0x4c // Keyboard Delete Forward +#define KEY_END 0x4d // Keyboard End +#define KEY_PAGEDOWN 0x4e // Keyboard Page Down +#define KEY_RIGHT 0x4f // Keyboard Right Arrow +#define KEY_LEFT 0x50 // Keyboard Left Arrow +#define KEY_DOWN 0x51 // Keyboard Down Arrow +#define KEY_UP 0x52 // Keyboard Up Arrow + +#define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear +#define KEY_KPSLASH 0x54 // Keypad / +#define KEY_KPASTERISK 0x55 // Keypad * +#define KEY_KPMINUS 0x56 // Keypad - +#define KEY_KPPLUS 0x57 // Keypad + +#define KEY_KPENTER 0x58 // Keypad ENTER +#define KEY_KP1 0x59 // Keypad 1 and End +#define KEY_KP2 0x5a // Keypad 2 and Down Arrow +#define KEY_KP3 0x5b // Keypad 3 and PageDn +#define KEY_KP4 0x5c // Keypad 4 and Left Arrow +#define KEY_KP5 0x5d // Keypad 5 +#define KEY_KP6 0x5e // Keypad 6 and Right Arrow +#define KEY_KP7 0x5f // Keypad 7 and Home +#define KEY_KP8 0x60 // Keypad 8 and Up Arrow +#define KEY_KP9 0x61 // Keypad 9 and Page Up +#define KEY_KP0 0x62 // Keypad 0 and Insert +#define KEY_KPDOT 0x63 // Keypad . and Delete + +#define KEY_102ND 0x64 // Keyboard Non-US \ and | +#define KEY_COMPOSE 0x65 // Keyboard Application +#define KEY_POWER 0x66 // Keyboard Power +#define KEY_KPEQUAL 0x67 // Keypad = + +#define KEY_F13 0x68 // Keyboard F13 +#define KEY_F14 0x69 // Keyboard F14 +#define KEY_F15 0x6a // Keyboard F15 +#define KEY_F16 0x6b // Keyboard F16 +#define KEY_F17 0x6c // Keyboard F17 +#define KEY_F18 0x6d // Keyboard F18 +#define KEY_F19 0x6e // Keyboard F19 +#define KEY_F20 0x6f // Keyboard F20 +#define KEY_F21 0x70 // Keyboard F21 +#define KEY_F22 0x71 // Keyboard F22 +#define KEY_F23 0x72 // Keyboard F23 +#define KEY_F24 0x73 // Keyboard F24 + +#define KEY_OPEN 0x74 // Keyboard Execute +#define KEY_HELP 0x75 // Keyboard Help +#define KEY_PROPS 0x76 // Keyboard Menu +#define KEY_FRONT 0x77 // Keyboard Select +#define KEY_STOP 0x78 // Keyboard Stop +#define KEY_AGAIN 0x79 // Keyboard Again +#define KEY_UNDO 0x7a // Keyboard Undo +#define KEY_CUT 0x7b // Keyboard Cut +#define KEY_COPY 0x7c // Keyboard Copy +#define KEY_PASTE 0x7d // Keyboard Paste +#define KEY_FIND 0x7e // Keyboard Find +#define KEY_MUTE 0x7f // Keyboard Mute +#define KEY_VOLUMEUP 0x80 // Keyboard Volume Up +#define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down +// 0x82 Keyboard Locking Caps Lock +// 0x83 Keyboard Locking Num Lock +// 0x84 Keyboard Locking Scroll Lock +#define KEY_KPCOMMA 0x85 // Keypad Comma +// 0x86 Keypad Equal Sign +#define KEY_RO 0x87 // Keyboard International1 +#define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2 +#define KEY_YEN 0x89 // Keyboard International3 +#define KEY_HENKAN 0x8a // Keyboard International4 +#define KEY_MUHENKAN 0x8b // Keyboard International5 +#define KEY_KPJPCOMMA 0x8c // Keyboard International6 +// 0x8d Keyboard International7 +// 0x8e Keyboard International8 +// 0x8f Keyboard International9 +#define KEY_HANGEUL 0x90 // Keyboard LANG1 +#define KEY_HANJA 0x91 // Keyboard LANG2 +#define KEY_KATAKANA 0x92 // Keyboard LANG3 +#define KEY_HIRAGANA 0x93 // Keyboard LANG4 +#define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5 +// 0x95 Keyboard LANG6 +// 0x96 Keyboard LANG7 +// 0x97 Keyboard LANG8 +// 0x98 Keyboard LANG9 +// 0x99 Keyboard Alternate Erase +// 0x9a Keyboard SysReq/Attention +// 0x9b Keyboard Cancel +// 0x9c Keyboard Clear +// 0x9d Keyboard Prior +// 0x9e Keyboard Return +// 0x9f Keyboard Separator +// 0xa0 Keyboard Out +// 0xa1 Keyboard Oper +// 0xa2 Keyboard Clear/Again +// 0xa3 Keyboard CrSel/Props +// 0xa4 Keyboard ExSel + +// 0xb0 Keypad 00 +// 0xb1 Keypad 000 +// 0xb2 Thousands Separator +// 0xb3 Decimal Separator +// 0xb4 Currency Unit +// 0xb5 Currency Sub-unit +#define KEY_KPLEFTPAREN 0xb6 // Keypad ( +#define KEY_KPRIGHTPAREN 0xb7 // Keypad ) +// 0xb8 Keypad { +// 0xb9 Keypad } +// 0xba Keypad Tab +// 0xbb Keypad Backspace +// 0xbc Keypad A +// 0xbd Keypad B +// 0xbe Keypad C +// 0xbf Keypad D +// 0xc0 Keypad E +// 0xc1 Keypad F +// 0xc2 Keypad XOR +// 0xc3 Keypad ^ +// 0xc4 Keypad % +// 0xc5 Keypad < +// 0xc6 Keypad > +// 0xc7 Keypad & +// 0xc8 Keypad && +// 0xc9 Keypad | +// 0xca Keypad || +// 0xcb Keypad : +// 0xcc Keypad # +// 0xcd Keypad Space +// 0xce Keypad @ +// 0xcf Keypad ! +// 0xd0 Keypad Memory Store +// 0xd1 Keypad Memory Recall +// 0xd2 Keypad Memory Clear +// 0xd3 Keypad Memory Add +// 0xd4 Keypad Memory Subtract +// 0xd5 Keypad Memory Multiply +// 0xd6 Keypad Memory Divide +// 0xd7 Keypad +/- +// 0xd8 Keypad Clear +// 0xd9 Keypad Clear Entry +// 0xda Keypad Binary +// 0xdb Keypad Octal +// 0xdc Keypad Decimal +// 0xdd Keypad Hexadecimal + +#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control +#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift +#define KEY_LEFTALT 0xe2 // Keyboard Left Alt +#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI +#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control +#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift +#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt +#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI + +#define KEY_MEDIA_PLAYPAUSE 0xe8 +#define KEY_MEDIA_STOPCD 0xe9 +#define KEY_MEDIA_PREVIOUSSONG 0xea +#define KEY_MEDIA_NEXTSONG 0xeb +#define KEY_MEDIA_EJECTCD 0xec +#define KEY_MEDIA_VOLUMEUP 0xed +#define KEY_MEDIA_VOLUMEDOWN 0xee +#define KEY_MEDIA_MUTE 0xef +#define KEY_MEDIA_WWW 0xf0 +#define KEY_MEDIA_BACK 0xf1 +#define KEY_MEDIA_FORWARD 0xf2 +#define KEY_MEDIA_STOP 0xf3 +#define KEY_MEDIA_FIND 0xf4 +#define KEY_MEDIA_SCROLLUP 0xf5 +#define KEY_MEDIA_SCROLLDOWN 0xf6 +#define KEY_MEDIA_EDIT 0xf7 +#define KEY_MEDIA_SLEEP 0xf8 +#define KEY_MEDIA_COFFEE 0xf9 +#define KEY_MEDIA_REFRESH 0xfa +#define KEY_MEDIA_CALC 0xfb + +#endif \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..91c5efd --- /dev/null +++ b/README.md @@ -0,0 +1,177 @@ +# Bibliothèque ADB pour Framework Arduino + +![Version](https://img.shields.io/badge/version-1.0.0-blue) +![Licence](https://img.shields.io/badge/licence-GNU%20GPL%20v3-green) +![État](https://img.shields.io/badge/état-Beta-orange) +![Framework](https://img.shields.io/badge/framework-Arduino-teal) + +> **Note de développement** : Cette bibliothèque est implémentée pour le framework Arduino et actuellement optimisée principalement pour la plateforme STM32. Le support pour d'autres plateformes Arduino est en cours de développement. + +## Vue d'ensemble + +La bibliothèque ADB est une implémentation complète du protocole Apple Desktop Bus (ADB) en C++ pour le framework Arduino, fonctionnant sur diverses cartes comme STM32, ESP32, Arduino AVR et Teensy. Elle permet d'interfacer facilement des périphériques Apple vintage (claviers, souris) avec des systèmes modernes. + +## Fonctionnalités clés + +- 🔌 **Compatible avec plusieurs plateformes** : STM32 (primaire), Arduino, ESP32, Teensy (en cours de développement) +- 🧩 **API unifiée** : même interface de programmation quelle que soit la plateforme +- ⌨️ **Support complet des claviers ADB** : lecture des touches, gestion des modificateurs, contrôle des LEDs +- 🖱️ **Support complet des souris ADB** : lecture des mouvements et des boutons +- 🔄 **Conversion ADB vers HID** : mappage des codes de touches ADB vers les codes HID standard +- 📊 **Détection automatique des périphériques** : avec gestion des erreurs robuste +- 🛠️ **Utilitaires avancés** : fonctions d'aide au développement et débogage +- 📝 **Documentation complète** : commentaires explicites et exemples documentés + +## Plateformes Arduino supportées + +| Plateforme | Pin par défaut | Testé sur | Statut | +|------------|---------------|----------|--------| +| STM32 (Arduino Core) | PB4 | STM32F103, Bluepill, STM32F401, STM32F411 | ✅ Testé et stable | +| Arduino AVR | 2 | Uno, Mega | ⚠️ Support préliminaire | +| ESP32 (Arduino Core) | 21 | ESP32-WROOM, ESP32-WROVER | ⚠️ En développement | +| Teensy (Arduino Core) | 3 | Teensy 3.2, 4.0 | ⚠️ En développement | +| Autres cartes Arduino | 2 | Diverses | ⛔ Non testé | + +## Installation + +### Pour l'IDE Arduino +1. Téléchargez la bibliothèque depuis le dépôt +2. Extrayez les fichiers dans le dossier `libraries` de votre IDE Arduino +3. Redémarrez l'IDE Arduino +4. La bibliothèque est maintenant disponible dans "Croquis > Inclure une bibliothèque" + +### Pour PlatformIO +1. Ajoutez la dépendance dans votre `platformio.ini`: + ```ini + lib_deps = + electronrare/ADB @ ^1.0.0 + ``` +2. Ou installez-la manuellement dans le dossier `lib/` de votre projet + +#### Configuration exemple pour STM32 (platformio.ini) +```ini +[env:bluepill_f103c8] +platform = ststm32 +board = bluepill_f103c8 +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 +upload_protocol = stlink +monitor_speed = 115200 +build_flags = + -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC + -D USBCON + -D USB_MANUFACTURER="\"L'électron rare\"" + -D USB_PRODUCT="\"ADB2USB HID\"" +``` + +#### Configuration exemple pour ESP32 (platformio.ini) +```ini +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 +monitor_speed = 115200 +``` + +## Utilisation basique dans le framework Arduino + +```cpp +#include +#include "adb.h" + +// Configuration selon la carte Arduino +#if defined(ARDUINO_ARCH_STM32) + #define ADB_PIN PB4 +#else + #define ADB_PIN 2 // Arduino, ESP32, etc. +#endif + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); + +void setup() { + Serial.begin(115200); + adb.init(); + Serial.println("ADB Initialized"); +} + +void loop() { + // Lecture du clavier via Arduino + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + if (!error && keyPress.data.key0 != 0) { + Serial.print("Key: 0x"); + Serial.println(keyPress.data.key0, HEX); + } + + // Lecture de la souris + error = false; + auto mouseData = devices.mouseReadData(&error); + if (!error) { + int8_t xMove = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t yMove = adbMouseConvertAxis(mouseData.data.y_offset); + // Traiter les données de mouvement... + } + + delay(20); // Intervalle de polling typique pour Arduino +} +``` + +## Exemples Arduino inclus + +La bibliothèque est fournie avec plusieurs exemples pratiques pour Arduino IDE et PlatformIO : + +- **basic_keyboard** : Utilisation simple d'un clavier ADB +- **basic_mouse** : Utilisation simple d'une souris ADB +- **multiplatform_basic** : Exemple multiplateforme (avec détection automatique) +- **keyboard_mouse_example** : Utilisation simultanée d'un clavier et d'une souris +- **multiplatform_event_handler** : Gestionnaire d'événements avancé +- **usb_hid_stm32** : Conversion ADB vers USB HID (STM32 uniquement) +- **multiplatform_device_info** : Scanner de périphériques ADB +- **platformio_stm32_example** : Exemple complet pour PlatformIO avec STM32 +- **platformio_esp32_example** : Exemple pour PlatformIO avec ESP32 + +## Structure du projet + +## Crédits et contributions + +### Auteurs +- **Clément SAILLANT** - L'électron rare - *Développeur principal* +- **Szymon Łopaciuk** - *Travail initial et inspiration* - [stm32-adb2usb](https://github.com/szymonlopaciuk/stm32-adb2usb) + +### Remerciements particuliers +- **Projet TMK** - Pour la documentation et le travail sur la conversion ADB-USB +- **Apple Developer Archives** - Pour la documentation technique sur le protocole ADB +- **Communauté r/VintageApple** - Pour le support et les tests + +### Testeurs +- L'équipe de L'électron rare +- Utilisateurs du forum STM32duino +- Membres de la communauté RetroComputing + +## Historique des versions + +- **1.0.0-beta** (Janvier 2025) + - Version initiale de la bibliothèque + - Support complet pour STM32 + - Support préliminaire pour d'autres plateformes + +## À venir + +- Support complet pour Arduino, ESP32, et Teensy +- Meilleure gestion de l'alimentation +- Support pour des périphériques ADB plus exotiques +- Interface graphique de configuration et diagnostic + +## Licence + +Ce projet est sous licence GNU GPL v3. Voir le fichier LICENSE pour plus de détails. + +--- + +Conçu avec ❤️ pour donner une nouvelle vie aux périphériques Apple vintage. + diff --git a/examples/auto_platform_detect.cpp b/examples/auto_platform_detect.cpp new file mode 100644 index 0000000..4040140 --- /dev/null +++ b/examples/auto_platform_detect.cpp @@ -0,0 +1,73 @@ +/** + * @file auto_platform_detect.cpp + * @brief Exemple avec détection automatique de la plateforme matérielle + * + * Ce fichier démontre comment la bibliothèque ADB peut être utilisée avec + * une détection automatique de la plateforme matérielle sous-jacente. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" + +// Détection automatique de plateforme et configuration de la broche +#if defined(ARDUINO_ARCH_AVR) + #define PLATFORM_NAME "Arduino AVR" + #define ADB_PIN 2 +#elif defined(ARDUINO_ARCH_SAM) + #define PLATFORM_NAME "Arduino SAM" + #define ADB_PIN 2 +#elif defined(ARDUINO_ARCH_SAMD) + #define PLATFORM_NAME "Arduino SAMD" + #define ADB_PIN 2 +#elif defined(ARDUINO_ARCH_STM32) + #define PLATFORM_NAME "STM32" + #define ADB_PIN PB4 +#elif defined(ARDUINO_ARCH_ESP32) + #define PLATFORM_NAME "ESP32" + #define ADB_PIN 21 +#elif defined(TEENSYDUINO) + #define PLATFORM_NAME "Teensy" + #define ADB_PIN 3 +#else + #define PLATFORM_NAME "Unknown Platform" + #define ADB_PIN 2 + #warning "Plateforme non reconnue, utilisation de la pin 2 par défaut" +#endif + +// Paramètres communs +constexpr uint32_t SERIAL_BAUD = 115200; +constexpr uint16_t POLL_INTERVAL = 250; // ms + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +void setup() { + // Configuration de la communication série + Serial.begin(SERIAL_BAUD); + while (!Serial && millis() < 3000); // Attente avec timeout + + // Initialisation du bus ADB + adb.init(); + + Serial.println(F("=== ADB Multiplateforme ===")); + Serial.print(F("Plateforme détectée: ")); + Serial.println(F(PLATFORM_NAME)); + Serial.print(F("Utilisation de la broche: ")); + Serial.println(ADB_PIN); + Serial.println(F("========================")); +} + +void loop() { + // Affichage des informations périphériques ADB + utils.printKeyboardStatus(); + utils.printMouseStatus(); + + Serial.println(F("------------------------")); + delay(POLL_INTERVAL); +} diff --git a/examples/multiplatform_basic.cpp b/examples/multiplatform_basic.cpp new file mode 100644 index 0000000..16c7069 --- /dev/null +++ b/examples/multiplatform_basic.cpp @@ -0,0 +1,91 @@ +/** + * @file multiplatform_basic.cpp + * @brief Exemple de base pour l'utilisation de la bibliothèque ADB sur plusieurs plateformes + * + * Cet exemple montre comment utiliser la bibliothèque ADB sur différentes plateformes + * en modifiant simplement la définition de la plateforme et la broche utilisée. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" + +// Définir la plateforme (décommenter une seule ligne) +#define PLATFORM_ARDUINO +// #define PLATFORM_STM32 +// #define PLATFORM_ESP32 +// #define PLATFORM_TEENSY + +// Configuration de la broche ADB selon la plateforme +#ifdef PLATFORM_ARDUINO + #define ADB_PIN 2 + #define PLATFORM_NAME "Arduino" +#elif defined(PLATFORM_STM32) + #define ADB_PIN PB4 + #define PLATFORM_NAME "STM32" +#elif defined(PLATFORM_ESP32) + #define ADB_PIN 21 + #define PLATFORM_NAME "ESP32" +#elif defined(PLATFORM_TEENSY) + #define ADB_PIN 3 + #define PLATFORM_NAME "Teensy" +#else + #error "Aucune plateforme définie! Décommentez une définition de plateforme." +#endif + +// Paramètres communs +constexpr uint32_t SERIAL_BAUD = 115200; +constexpr uint16_t POLL_INTERVAL = 250; // ms + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); + +void setup() { + // Configuration de la communication série + Serial.begin(SERIAL_BAUD); + while (!Serial && millis() < 3000); // Attente du port série avec timeout + + // Initialisation du bus ADB + adb.init(); + + Serial.print(F("ADB Initialized on ")); + Serial.println(F(PLATFORM_NAME)); + Serial.println(F("Bibliothèque ADB multiplateforme")); +} + +void loop() { + bool error = false; + + // Tentative de lecture du clavier + auto keyPress = devices.keyboardReadKeyPress(&error); + if (!error && (keyPress.data.key0 != 0)) { + Serial.print(F("Keyboard - Key: 0x")); + Serial.print(keyPress.data.key0, HEX); + Serial.print(F(" (")); + Serial.print(keyPress.data.released0 ? F("Released") : F("Pressed")); + Serial.println(F(")")); + } + + // Tentative de lecture de la souris + error = false; + auto mouseData = devices.mouseReadData(&error); + if (!error) { + int8_t xMove = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t yMove = adbMouseConvertAxis(mouseData.data.y_offset); + + if (xMove != 0 || yMove != 0 || mouseData.data.button) { + Serial.print(F("Mouse - X: ")); + Serial.print(xMove); + Serial.print(F(", Y: ")); + Serial.print(yMove); + Serial.print(F(", Button: ")); + Serial.println(mouseData.data.button ? F("Pressed") : F("Released")); + } + } + + delay(POLL_INTERVAL); +} diff --git a/examples/multiplatform_device_info.cpp b/examples/multiplatform_device_info.cpp new file mode 100644 index 0000000..83b79e9 --- /dev/null +++ b/examples/multiplatform_device_info.cpp @@ -0,0 +1,167 @@ +/** + * @file multiplatform_device_info.cpp + * @brief Exemple de récupération d'informations sur les périphériques ADB + * + * Ce programme permet de détecter et d'afficher des informations sur les périphériques + * ADB connectés. Il est compatible avec toutes les plateformes supportées. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" + +// Configuration multiplateforme +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) + #define ADB_PIN 2 + #define PLATFORM_NAME "Arduino" +#elif defined(ARDUINO_ARCH_STM32) + #define ADB_PIN PB4 + #define PLATFORM_NAME "STM32" +#elif defined(ARDUINO_ARCH_ESP32) + #define ADB_PIN 21 + #define PLATFORM_NAME "ESP32" +#elif defined(TEENSYDUINO) + #define ADB_PIN 3 + #define PLATFORM_NAME "Teensy" +#else + #define ADB_PIN 2 + #define PLATFORM_NAME "Plateforme inconnue" +#endif + +// Adresses ADB à tester +constexpr uint8_t DEVICE_ADDRESSES[] = {2, 3, 4, 5, 6, 7}; +constexpr uint8_t NUM_ADDRESSES = sizeof(DEVICE_ADDRESSES) / sizeof(DEVICE_ADDRESSES[0]); + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); + +void setup() { + // Configuration de la communication série + Serial.begin(115200); + while (!Serial && millis() < 3000); + + Serial.println(F("=== Scanner de périphériques ADB ===")); + Serial.print(F("Plateforme: ")); + Serial.print(F(PLATFORM_NAME)); + Serial.print(F(", Broche ADB: ")); + Serial.println(ADB_PIN); + + // Initialisation du bus ADB + adb.init(); + delay(500); // Attendre que le bus se stabilise + + Serial.println(F("Recherche de périphériques ADB...")); + Serial.println(F("--------------------------------")); + + // Tentative de détection de périphériques + bool deviceFound = false; + + for (uint8_t i = 0; i < NUM_ADDRESSES; i++) { + uint8_t addr = DEVICE_ADDRESSES[i]; + bool error = false; + bool devicePresent = false; + + Serial.print(F("Adresse 0x")); + Serial.print(addr, HEX); + Serial.print(F(" : ")); + + // Essai de lecture du registre 3 + adb_data reg3 = {0}; + adb.writeCommand(ADBProtocol::CMD_TALK | ADBProtocol::ADDRESS(addr) | ADBProtocol::REGISTER(3)); + adb.waitTLT(true); + bool success = adb.readDataPacket(®3.raw, 16); + + if (success) { + Serial.println(F("Périphérique détecté!")); + Serial.print(F(" Handler ID: 0x")); + Serial.println(reg3.data.device_handler_id, HEX); + + // Identification du type de périphérique + if (addr == ADDR_KEYBOARD) { + Serial.println(F(" Type: Clavier ADB")); + // Tentative de lecture des modificateurs + bool keyError = false; + auto modifiers = devices.keyboardReadModifiers(&keyError); + if (!keyError) { + Serial.println(F(" État Caps Lock: ") + + String(modifiers.data.caps_lock ? "Activé" : "Désactivé")); + } + } + else if (addr == ADDR_MOUSE) { + Serial.println(F(" Type: Souris ADB")); + } + else { + Serial.print(F(" Type: Autre périphérique ADB (")); + Serial.print(addr); + Serial.println(F(")")); + } + + deviceFound = true; + } else { + Serial.println(F("Aucun périphérique")); + } + } + + if (!deviceFound) { + Serial.println(F("Aucun périphérique ADB détecté.")); + Serial.println(F("Vérifiez les connexions et l'alimentation.")); + } + + Serial.println(F("--------------------------------")); + Serial.println(F("Scan terminé. Surveillance active...")); +} + +void loop() { + // Surveillance continue des périphériques + bool keyboardError = false; + bool mouseError = false; + static bool keyboardPresent = false; + static bool mousePresent = false; + + // Vérification du clavier + auto keyPress = devices.keyboardReadKeyPress(&keyboardError); + if (!keyboardError) { + if (!keyboardPresent) { + Serial.println(F("Clavier ADB connecté")); + keyboardPresent = true; + } + + // Affichage des touches pressées + if (keyPress.data.key0 != 0 && !keyPress.data.released0) { + Serial.print(F("Touche: 0x")); + Serial.println(keyPress.data.key0, HEX); + } + } else if (keyboardPresent) { + Serial.println(F("Clavier ADB déconnecté")); + keyboardPresent = false; + } + + // Vérification de la souris + auto mouseData = devices.mouseReadData(&mouseError); + if (!mouseError) { + if (!mousePresent) { + Serial.println(F("Souris ADB connectée")); + mousePresent = true; + } + + // Affichage des mouvements significatifs + int8_t xMove = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t yMove = adbMouseConvertAxis(mouseData.data.y_offset); + + if (abs(xMove) > 5 || abs(yMove) > 5) { + Serial.print(F("Mouvement: X=")); + Serial.print(xMove); + Serial.print(F(", Y=")); + Serial.println(yMove); + } + } else if (mousePresent) { + Serial.println(F("Souris ADB déconnectée")); + mousePresent = false; + } + + delay(100); +} diff --git a/examples/multiplatform_event_handler.cpp b/examples/multiplatform_event_handler.cpp new file mode 100644 index 0000000..b8d1604 --- /dev/null +++ b/examples/multiplatform_event_handler.cpp @@ -0,0 +1,144 @@ +/** + * @file multiplatform_event_handler.cpp + * @brief Gestionnaire d'événements ADB multiplateforme + * + * Cet exemple implémente un gestionnaire d'événements avancé pour les périphériques ADB. + * Il est compatible avec toutes les plateformes prises en charge et montre comment traiter + * les événements du clavier et de la souris de manière événementielle. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" + +// Configuration spécifique à la plateforme +#if defined(ARDUINO_ARCH_AVR) + #define ADB_PIN 2 +#elif defined(ARDUINO_ARCH_STM32) + #define ADB_PIN PB4 +#elif defined(ARDUINO_ARCH_ESP32) + #define ADB_PIN 21 +#elif defined(TEENSYDUINO) + #define ADB_PIN 3 +#else + #define ADB_PIN 2 +#endif + +// Paramètres communs +constexpr uint32_t SERIAL_BAUD = 115200; +constexpr uint16_t POLL_INTERVAL = 20; // Intervalle court pour la réactivité + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); + +// État du système +uint8_t lastKeys[2] = {0, 0}; +bool lastReleased[2] = {true, true}; +int8_t lastX = 0, lastY = 0; +bool lastButton = false; + +// Fonctions de traitement des événements +void onKeyPressed(uint8_t keyCode) { + Serial.print(F("Touche pressée: 0x")); + Serial.print(keyCode, HEX); + + // Affichage du code HID équivalent si disponible + uint8_t hidCode = ADBKeymap::toHID(keyCode); + if (hidCode != KEY_NONE) { + Serial.print(F(" (HID: 0x")); + Serial.print(hidCode, HEX); + Serial.print(F(")")); + } + + Serial.println(); +} + +void onKeyReleased(uint8_t keyCode) { + Serial.print(F("Touche relâchée: 0x")); + Serial.println(keyCode, HEX); +} + +void onMouseMove(int8_t deltaX, int8_t deltaY) { + Serial.print(F("Souris - Mouvement: (")); + Serial.print(deltaX); + Serial.print(F(", ")); + Serial.print(deltaY); + Serial.println(F(")")); +} + +void onMouseButtonChange(bool isPressed) { + Serial.print(F("Souris - Bouton: ")); + Serial.println(isPressed ? F("Pressé") : F("Relâché")); +} + +void setup() { + // Configuration de la communication série + Serial.begin(SERIAL_BAUD); + while (!Serial && millis() < 3000); + + // Initialisation du bus ADB + adb.init(); + Serial.println(F("Gestionnaire d'événements ADB multiplateforme initialisé")); + Serial.println(F("En attente d'événements...")); +} + +void loop() { + // Traitement des événements clavier + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + + if (!error) { + // Vérification de la première touche + if (keyPress.data.key0 != lastKeys[0] || keyPress.data.released0 != lastReleased[0]) { + if (keyPress.data.key0 != 0) { + if (!keyPress.data.released0 && lastReleased[0]) { + onKeyPressed(keyPress.data.key0); + } else if (keyPress.data.released0 && !lastReleased[0]) { + onKeyReleased(keyPress.data.key0); + } + } + lastKeys[0] = keyPress.data.key0; + lastReleased[0] = keyPress.data.released0; + } + + // Vérification de la seconde touche + if (keyPress.data.key1 != lastKeys[1] || keyPress.data.released1 != lastReleased[1]) { + if (keyPress.data.key1 != 0) { + if (!keyPress.data.released1 && lastReleased[1]) { + onKeyPressed(keyPress.data.key1); + } else if (keyPress.data.released1 && !lastReleased[1]) { + onKeyReleased(keyPress.data.key1); + } + } + lastKeys[1] = keyPress.data.key1; + lastReleased[1] = keyPress.data.released1; + } + } + + // Traitement des événements souris + error = false; + auto mouseData = devices.mouseReadData(&error); + + if (!error) { + int8_t xMove = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t yMove = adbMouseConvertAxis(mouseData.data.y_offset); + + // Détection du mouvement + if (xMove != 0 || yMove != 0) { + onMouseMove(xMove, yMove); + } + + // Détection du changement d'état du bouton + if (mouseData.data.button != lastButton) { + onMouseButtonChange(mouseData.data.button); + lastButton = mouseData.data.button; + } + } + + // Fréquence de polling élevée pour une bonne réactivité + delay(POLL_INTERVAL); +} diff --git a/examples/platform_aware_example.cpp b/examples/platform_aware_example.cpp new file mode 100644 index 0000000..a72a7f5 --- /dev/null +++ b/examples/platform_aware_example.cpp @@ -0,0 +1,55 @@ +/** + * @file platform_aware_example.cpp + * @brief Exemple utilisant la détection automatique de plateforme + * + * Cet exemple démontre l'utilisation du fichier ADBPlatform.h pour + * créer un code qui s'adapte automatiquement à la plateforme cible. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" +#include "ADBPlatform.h" + +// Utilisation des paramètres de la plateforme +ADB adb(ADB_DEFAULT_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +void setup() { + // Configuration adaptée à la plateforme + Serial.begin(ADB_SERIAL_BAUD); + while (!Serial && millis() < 3000); + + adb.init(); + + // Affichage des informations de plateforme + printPlatformInfo(); + Serial.println(F("Initialisation ADB réussie")); + + // Configuration spécifique selon la plateforme + #ifdef ADB_PLATFORM_ESP32 + // Code spécifique pour ESP32 + Serial.println(F("Configuration spécifique pour ESP32 appliquée")); + #elif defined(ADB_PLATFORM_STM32) + // Code spécifique pour STM32 + Serial.println(F("Configuration spécifique pour STM32 appliquée")); + #elif defined(ADB_PLATFORM_AVR) + // Code spécifique pour Arduino AVR + Serial.println(F("Mode économie de mémoire activé pour AVR")); + #endif + + Serial.println(F("Prêt à recevoir les événements ADB")); +} + +void loop() { + // Lecture des périphériques + utils.printKeyboardStatus(); + utils.printMouseStatus(); + + // Utilisation de l'intervalle adapté à la plateforme + delay(ADB_POLL_INTERVAL); +} diff --git a/examples/platformio_BLE_esp32_example/README.md b/examples/platformio_BLE_esp32_example/README.md new file mode 100644 index 0000000..8b84de6 --- /dev/null +++ b/examples/platformio_BLE_esp32_example/README.md @@ -0,0 +1,125 @@ +# 🔌 ADB2BLE Adapter pour ESP32 + +[![Licence: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![Plateforme: ESP32](https://img.shields.io/badge/Plateforme-ESP32-green.svg)](https://www.espressif.com/en/products/socs/esp32) +[![Framework: Arduino](https://img.shields.io/badge/Framework-Arduino-blue.svg)](https://www.arduino.cc/) +[![IDE: PlatformIO](https://img.shields.io/badge/IDE-PlatformIO-orange.svg)](https://platformio.org/) + +> Ce projet permet de connecter des périphériques Apple Desktop Bus (ADB) comme les claviers et souris Apple vintage à des appareils modernes via Bluetooth Low Energy (BLE). + +

+ ADB2BLE Adapter +

+ +## 📋 Sommaire +- [Fonctionnalités](#-fonctionnalités) +- [Prérequis](#-prérequis) +- [Schéma de connexion](#-schéma-de-connexion) +- [Installation](#-installation) +- [Utilisation](#-utilisation) +- [Dépannage](#-dépannage) +- [Détails techniques](#-détails-techniques) +- [Licence et auteur](#-licence-et-auteur) +- [Contributions](#-contributions) + +## ✨ Fonctionnalités + +- ✅ Conversion des signaux ADB vers BLE HID +- ⌨️ Support des claviers ADB +- 🖱️ Support des souris ADB +- 🔍 Reconnaissance automatique des périphériques +- 🔄 Reconnnexion automatique en cas de déconnexion +- ⚡ Faible latence (50Hz de taux de rafraîchissement) + +## 🛠️ Prérequis + +### Matériel +- ESP32 (testé sur ESP32-WROOM-32) +- Adaptateur de niveau logique 5V/3.3V ou simple résistance de 1kΩ +- Connecteur ADB (Mini-DIN 4 broches) ou adaptateur +- Câbles et connecteurs + +### Logiciel +- PlatformIO +- Arduino IDE (version 2.x ou supérieure) +- Bibliothèques requises: + - ADB (fournie dans ce dépôt) + - Arduino BLE HID (intégrée à l'ESP32) + +## 🔌 Schéma de connexion + +┌─────────┐ ┌─────────┐ +│ │ │ │ +│ ESP32 │ │ ADB │ +│ │ │ │ +├─────────┤ ├─────────┤ +│ GPIO21 ├───[Résistance 1kΩ]────────────────┤ DATA │ +│ │ │ │ │ +│ │ │ │ │ +│ │ pull-up 4.7-10kΩ │ │ +│ │ │ │ │ +│ 3.3V ├───────────────────────────────────┤ +5V │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ GND ├───────────────────────────────────┤ GND │ +└─────────┘ └─────────┘ + +⚠️ **Attention**: +- Vérifiez la tolérance de votre ESP32 avant de connecter directement l'alimentation 5V d'ADB. +- **Important**: Une résistance de pull-up (entre 4.7kΩ et 10kΩ) doit être connectée entre la ligne DATA et +5V pour assurer le bon fonctionnement du bus ADB. + +## 📥 Installation + +1. Clonez ce dépôt: + ```bash + git clone https://github.com/electron_rare/stm32-adb2usb.git + cd stm32-adb2usb + ``` + +2. Ouvrez le projet dans PlatformIO IDE: + - Sélectionnez "Open Project" + - Naviguez jusqu'au dossier `stm32-adb2usb/lib/adb/examples/platformio_BLE_esp32_example` + +3. Compilez et téléversez le projet sur votre ESP32: + ```bash + pio run --target upload + ``` + +## 📝 Utilisation + +1. Connectez vos périphériques ADB à l'adaptateur +2. Allumez l'ESP32 +3. Recherchez le périphérique Bluetooth "ADB2BLE Adapter" depuis votre ordinateur ou appareil mobile +4. Connectez-vous à l'adaptateur +5. Utilisez vos périphériques ADB comme des périphériques Bluetooth standard + +## ❓ Dépannage + +| Problème | Solution | +|----------|----------| +| **Périphérique non détecté** | Vérifiez les connexions ADB et assurez-vous que les périphériques sont alimentés | +| **Mouvement de souris irrégulier** | Ajustez les valeurs de conversion de mouvement dans le code | +| **Touches de clavier incorrectes** | Consultez la correspondance des touches dans la bibliothèque ADB | + +## 📊 Détails techniques + +- **Taux de rafraîchissement**: 50Hz (configurable via `POLL_INTERVAL`) +- **Consommation électrique**: ~30-50mA en fonctionnement normal +- **Compatibilité**: La plupart des périphériques ADB standards + +## 📄 Licence et auteur + +Développé par **Clément SAILLANT** - *L'électron rare* + +Copyright (C) 2025 Clément SAILLANT + +Ce projet est sous licence [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). + +## 👥 Contributions + +Les contributions sont les bienvenues! N'hésitez pas à: + +- Ouvrir des [issues](https://github.com/electron_rare/stm32-adb2usb/issues) +- Soumettre des [pull requests](https://github.com/electron_rare/stm32-adb2usb/pulls) +- Partager vos retours d'expérience diff --git a/examples/platformio_BLE_esp32_example/platformio.ini b/examples/platformio_BLE_esp32_example/platformio.ini new file mode 100644 index 0000000..5a0d532 --- /dev/null +++ b/examples/platformio_BLE_esp32_example/platformio.ini @@ -0,0 +1,7 @@ +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + electronrare/ADB +monitor_speed = 115200 diff --git a/examples/platformio_BLE_esp32_example/src/main.cpp b/examples/platformio_BLE_esp32_example/src/main.cpp new file mode 100644 index 0000000..8a608e2 --- /dev/null +++ b/examples/platformio_BLE_esp32_example/src/main.cpp @@ -0,0 +1,352 @@ +/** + * @file main.cpp + * @brief Exemple PlatformIO pour ESP32 avec la bibliothèque ADB + * + * Cet exemple montre comment utiliser la bibliothèque ADB dans un projet PlatformIO + * avec l'ESP32 pour lire et traiter les données des périphériques ADB. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include +#include +#include +#include +#include + +// Configuration pour ESP32 +constexpr uint8_t ADB_PIN = 21; // Pin de données ADB +constexpr uint16_t POLL_INTERVAL = 20; // 20ms (50Hz) +constexpr const char* DEVICE_NAME = "ADB2BLE Adapter"; + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// BLE HID +BLEHIDDevice* hid; +BLECharacteristic* inputKeyboard; +BLECharacteristic* inputMouse; +bool connected = false; + +// États +bool keyboardConnected = false; +bool mouseConnected = false; +uint8_t keyboardReport[8] = {0}; // Modificateurs + touches +uint8_t mouseReport[4] = {0}; // Bouton, X, Y, Wheel + +// Variables pour le suivi des mouvements de souris +int16_t accumulatedX = 0; +int16_t accumulatedY = 0; + +// Callback pour la connexion BLE +class ServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* server) { + connected = true; + Serial.println(F("Client BLE connecté")); + } + + void onDisconnect(BLEServer* server) { + connected = false; + Serial.println(F("Client BLE déconnecté")); + BLEDevice::startAdvertising(); + } +}; + +void setupBLE() { + // Initialisation du BLE + BLEDevice::init(DEVICE_NAME); + BLEServer* server = BLEDevice::createServer(); + server->setCallbacks(new ServerCallbacks()); + + // Création du service HID + hid = new BLEHIDDevice(server); + inputKeyboard = hid->inputReport(1); // Report ID 1: Keyboard + inputMouse = hid->inputReport(2); // Report ID 2: Mouse + + // Configuration des descripteurs HID + hid->manufacturer()->setValue("L'électron rare"); + hid->pnp(0x02, 0x05AC, 0x8240, 0x0001); // Apple + hid->hidInfo(0x00, 0x01); + + // Définition des descripteurs de rapport + const uint8_t reportMapKeyboard[] = { + // Clavier + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (1) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0xE0, // Usage Minimum (224) + 0x29, 0xE7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x01, // Input (Constant) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x65, // Usage Maximum (101) + 0x81, 0x00, // Input (Data, Array) + 0xC0, // End Collection + + // Souris + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x02, // Report ID (2) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Physical) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x03, // Usage Maximum (3) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x03, // Report Count (3) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x95, 0x01, // Report Count (1) + 0x75, 0x05, // Report Size (5) + 0x81, 0x01, // Input (Constant) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x03, // Report Count (3) + 0x81, 0x06, // Input (Data, Variable, Relative) + 0xC0, // End Collection + 0xC0 // End Collection + }; + + hid->reportMap((uint8_t*)reportMapKeyboard, sizeof(reportMapKeyboard)); + + // Activation du service + hid->startServices(); + + // Démarrage de la publicité BLE + BLEAdvertising* advertising = server->getAdvertising(); + advertising->setAppearance(HID_KEYBOARD); + advertising->addServiceUUID(hid->hidService()->getUUID()); + advertising->start(); + + Serial.println(F("BLE HID prêt, en attente de connexion")); +} + +void setup() { + // Initialisation de la communication série + Serial.begin(115200); + delay(1000); + + Serial.println(F("ADB2BLE pour ESP32 - PlatformIO")); + Serial.println(F("Bibliothèque ADB multiplateforme")); + + // Initialisation du bus ADB + adb.init(); + + // Configuration BLE + setupBLE(); + + // Détection des périphériques ADB + bool error; + + // Test du clavier + auto kbData = devices.keyboardReadModifiers(&error); + keyboardConnected = !error; + + // Test de la souris + error = false; + auto mouseData = devices.mouseReadData(&error); + mouseConnected = !error; + + Serial.print(F("Périphériques détectés - Clavier: ")); + Serial.print(keyboardConnected ? F("Oui") : F("Non")); + Serial.print(F(", Souris: ")); + Serial.println(mouseConnected ? F("Oui") : F("Non")); + + Serial.println(F("Conversion ADB->BLE active")); +} + +void handleKeyboard() { + if (!keyboardConnected || !connected) return; + + bool error = false; + static uint8_t lastModifiers = 0; + static uint8_t lastKeys[6] = {0}; + bool reportChanged = false; + + // Lecture des touches + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) { + keyboardConnected = false; + Serial.println(F("Clavier ADB déconnecté")); + return; + } + + // Lecture des modificateurs + auto modifiers = devices.keyboardReadModifiers(&error); + if (error) return; + + // Construction du rapport clavier BLE HID + uint8_t modByte = 0; + if (modifiers.data.shift) modByte |= KEY_MOD_LSHIFT; + if (modifiers.data.control) modByte |= KEY_MOD_LCTRL; + if (modifiers.data.option) modByte |= KEY_MOD_LALT; + if (modifiers.data.command) modByte |= KEY_MOD_LMETA; + + if (modByte != lastModifiers) { + lastModifiers = modByte; + reportChanged = true; + } + + // Effacement des touches + memset(&keyboardReport[2], 0, 6); + + // Ajout des touches + uint8_t keyIdx = 0; + + if (keyPress.data.key0 && !keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key0)) { + keyboardReport[2 + keyIdx++] = hidCode; + } + } + + if (keyPress.data.key1 && !keyPress.data.released1) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key1); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key1)) { + keyboardReport[2 + keyIdx++] = hidCode; + } + } + + // Vérification des changements + for (uint8_t i = 0; i < 6; i++) { + if (keyboardReport[2 + i] != lastKeys[i]) { + lastKeys[i] = keyboardReport[2 + i]; + reportChanged = true; + } + } + + // Envoi du rapport si nécessaire + if (reportChanged) { + keyboardReport[0] = modByte; + keyboardReport[1] = 0; // Reserved byte + inputKeyboard->setValue(keyboardReport, 8); + inputKeyboard->notify(); + } +} + +void handleMouse() { + if (!mouseConnected || !connected) return; + + bool error = false; + static bool lastButton = false; + + // Lecture des données de la souris + auto mouseData = devices.mouseReadData(&error); + if (error) { + mouseConnected = false; + Serial.println(F("Souris ADB déconnectée")); + return; + } + + // Conversion des valeurs + int8_t deltaX = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t deltaY = adbMouseConvertAxis(mouseData.data.y_offset); + bool button = mouseData.data.button; + + // Accumulation des petits mouvements + accumulatedX += deltaX; + accumulatedY += deltaY; + + // Envoi du rapport souris si mouvement ou changement de bouton + if (accumulatedX != 0 || accumulatedY != 0 || button != lastButton) { + // Limitation des valeurs + int8_t reportX = (accumulatedX > 127) ? 127 : ((accumulatedX < -127) ? -127 : accumulatedX); + int8_t reportY = (accumulatedY > 127) ? 127 : ((accumulatedY < -127) ? -127 : accumulatedY); + + // Construction du rapport + mouseReport[0] = button ? 1 : 0; // Bouton gauche + mouseReport[1] = reportX; + mouseReport[2] = reportY; + mouseReport[3] = 0; // Molette + + // Envoi du rapport + inputMouse->setValue(mouseReport, 4); + inputMouse->notify(); + + // Mise à jour de l'état + lastButton = button; + + // Réinitialisation des accumulateurs après envoi + accumulatedX -= reportX; + accumulatedY -= reportY; + } +} + +void reconnectDevices() { + static uint32_t lastReconnectAttempt = 0; + uint32_t now = millis(); + + // Tentative de reconnexion toutes les 3 secondes + if (now - lastReconnectAttempt > 3000) { + lastReconnectAttempt = now; + + bool shouldReconnect = false; + + if (!keyboardConnected) { + bool error = false; + devices.keyboardReadModifiers(&error); + if (!error) { + keyboardConnected = true; + Serial.println(F("Clavier ADB reconnecté")); + shouldReconnect = true; + } + } + + if (!mouseConnected) { + bool error = false; + devices.mouseReadData(&error); + if (!error) { + mouseConnected = true; + Serial.println(F("Souris ADB reconnectée")); + shouldReconnect = true; + } + } + + if (shouldReconnect) { + Serial.println(F("Périphériques ADB actifs:")); + Serial.print(F("- Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("- Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); + } + } +} + +void loop() { + // Lecture et conversion des périphériques ADB + handleKeyboard(); + handleMouse(); + + // Reconnexion si nécessaire + if (!keyboardConnected || !mouseConnected) { + reconnectDevices(); + } + + // Délai de polling optimal + delay(POLL_INTERVAL); +} diff --git a/examples/platformio_USB_arduino_uno_example/platformio.ini b/examples/platformio_USB_arduino_uno_example/platformio.ini new file mode 100644 index 0000000..920d094 --- /dev/null +++ b/examples/platformio_USB_arduino_uno_example/platformio.ini @@ -0,0 +1,9 @@ +[env:uno] +platform = atmelavr +board = uno +framework = arduino +monitor_speed = 9600 + +lib_deps = + electronrare/ADB @ ^1.0.0 + electronrare/USBHID @ ^1.0.0 diff --git a/examples/platformio_USB_arduino_uno_example/src/main.cpp b/examples/platformio_USB_arduino_uno_example/src/main.cpp new file mode 100644 index 0000000..724f994 --- /dev/null +++ b/examples/platformio_USB_arduino_uno_example/src/main.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +// Configuration pour Arduino Uno +constexpr uint8_t ADB_PIN = 2; // Pin de données ADB +constexpr uint16_t POLL_INTERVAL = 10; // 10ms (100Hz) + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// États +bool keyboardConnected = false; +bool mouseConnected = false; +uint8_t keyboardReport[8] = {0}; // Modificateurs + touches +uint8_t mouseReport[4] = {0}; // Bouton, X, Y, Wheel + +void setup() { + Serial.begin(9600); + delay(1000); + + Serial.println(F("ADB2USB pour Arduino Uno - PlatformIO")); + adb.init(); + USBHID_begin(true, true); + + bool error; + keyboardConnected = !devices.keyboardReadModifiers(&error); + mouseConnected = !devices.mouseReadData(&error); + + Serial.print(F("Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); +} + +void loop() { + if (keyboardConnected) { + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) keyboardConnected = false; + // ...traitement clavier... + } + + if (mouseConnected) { + bool error = false; + auto mouseData = devices.mouseReadData(&error); + if (error) mouseConnected = false; + // ...traitement souris... + } + + delay(POLL_INTERVAL); +} diff --git a/examples/platformio_USB_esp32_example/platformio.ini b/examples/platformio_USB_esp32_example/platformio.ini new file mode 100644 index 0000000..43b29cf --- /dev/null +++ b/examples/platformio_USB_esp32_example/platformio.ini @@ -0,0 +1,9 @@ +[env:esp32] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 + +lib_deps = + electronrare/ADB @ ^1.0.0 + electronrare/USBHID @ ^1.0.0 diff --git a/examples/platformio_USB_esp32_example/src/main.cpp b/examples/platformio_USB_esp32_example/src/main.cpp new file mode 100644 index 0000000..5fa194c --- /dev/null +++ b/examples/platformio_USB_esp32_example/src/main.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +// Configuration pour ESP32 +constexpr uint8_t ADB_PIN = 21; // Pin de données ADB +constexpr uint16_t POLL_INTERVAL = 10; // 10ms (100Hz) + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// États +bool keyboardConnected = false; +bool mouseConnected = false; +uint8_t keyboardReport[8] = {0}; // Modificateurs + touches +uint8_t mouseReport[4] = {0}; // Bouton, X, Y, Wheel + +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(F("ADB2USB pour ESP32 - PlatformIO")); + adb.init(); + USBHID_begin(true, true); + + bool error; + keyboardConnected = !devices.keyboardReadModifiers(&error); + mouseConnected = !devices.mouseReadData(&error); + + Serial.print(F("Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); +} + +void loop() { + if (keyboardConnected) { + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) keyboardConnected = false; + // ...traitement clavier... + } + + if (mouseConnected) { + bool error = false; + auto mouseData = devices.mouseReadData(&error); + if (error) mouseConnected = false; + // ...traitement souris... + } + + delay(POLL_INTERVAL); +} diff --git a/examples/platformio_USB_leonardo_example/platformio.ini b/examples/platformio_USB_leonardo_example/platformio.ini new file mode 100644 index 0000000..d1c4133 --- /dev/null +++ b/examples/platformio_USB_leonardo_example/platformio.ini @@ -0,0 +1,9 @@ +[env:leonardo] +platform = atmelavr +board = leonardo +framework = arduino +monitor_speed = 9600 + +lib_deps = + electronrare/ADB @ ^1.0.0 + HID-Project @ ^2.6.0 diff --git a/examples/platformio_USB_leonardo_example/src/main.cpp b/examples/platformio_USB_leonardo_example/src/main.cpp new file mode 100644 index 0000000..d9b9ed3 --- /dev/null +++ b/examples/platformio_USB_leonardo_example/src/main.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include // Bibliothèque HID pour Arduino Leonardo/Micro + +// Configuration pour Arduino Leonardo/Micro +constexpr uint8_t ADB_PIN = 2; // Pin de données ADB +constexpr uint16_t POLL_INTERVAL = 10; // 10ms (100Hz) + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// États +bool keyboardConnected = false; +bool mouseConnected = false; +uint8_t keyboardReport[8] = {0}; // Modificateurs + touches +uint8_t mouseReport[4] = {0}; // Bouton, X, Y, Wheel + +void setup() { + Serial.begin(9600); + delay(1000); + + Serial.println(F("ADB2USB pour Arduino Leonardo/Micro - PlatformIO")); + adb.init(); + + // Initialisation HID + Keyboard.begin(); + Mouse.begin(); + + bool error; + keyboardConnected = !devices.keyboardReadModifiers(&error); + mouseConnected = !devices.mouseReadData(&error); + + Serial.print(F("Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); +} + +void loop() { + if (keyboardConnected) { + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) { + keyboardConnected = false; + Serial.println(F("Clavier déconnecté")); + } else { + // Traitement des touches + if (keyPress.data.key0 && !keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE) { + Keyboard.press(hidCode); + } + } + if (keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE) { + Keyboard.release(hidCode); + } + } + } + } + + if (mouseConnected) { + bool error = false; + auto mouseData = devices.mouseReadData(&error); + if (error) { + mouseConnected = false; + Serial.println(F("Souris déconnectée")); + } else { + // Traitement des mouvements de souris + int8_t deltaX = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t deltaY = adbMouseConvertAxis(mouseData.data.y_offset); + bool button = mouseData.data.button; + + Mouse.move(deltaX, deltaY); + if (button) { + Mouse.press(MOUSE_LEFT); + } else { + Mouse.release(MOUSE_LEFT); + } + } + } + + delay(POLL_INTERVAL); +} diff --git a/examples/platformio_USB_multi_platform_example/platformio.ini b/examples/platformio_USB_multi_platform_example/platformio.ini new file mode 100644 index 0000000..ec8682f --- /dev/null +++ b/examples/platformio_USB_multi_platform_example/platformio.ini @@ -0,0 +1,27 @@ +[env:stm32] +platform = ststm32 +board = bluepill_f103c8 +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 + electronrare/USBHID @ ^1.0.0 +upload_protocol = stlink +monitor_speed = 115200 + +[env:esp32] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 + electronrare/USBHID @ ^1.0.0 +monitor_speed = 115200 + +[env:leonardo] +platform = atmelavr +board = leonardo +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 + HID-Project @ ^2.6.0 +monitor_speed = 9600 diff --git a/examples/platformio_USB_multi_platform_example/src/main.cpp b/examples/platformio_USB_multi_platform_example/src/main.cpp new file mode 100644 index 0000000..95f9904 --- /dev/null +++ b/examples/platformio_USB_multi_platform_example/src/main.cpp @@ -0,0 +1,127 @@ +#include +#include +#include + +#ifdef ARDUINO_ARCH_STM32 +#include // STM32 USB HID +#elif defined(ARDUINO_ARCH_ESP32) +#include // ESP32 USB HID +#elif defined(ARDUINO_ARCH_AVR) +#include // Arduino Leonardo/Micro HID +#endif + +// Configuration des broches et paramètres +#ifdef ARDUINO_ARCH_STM32 +constexpr uint8_t ADB_PIN = PB4; // STM32 +#elif defined(ARDUINO_ARCH_ESP32) +constexpr uint8_t ADB_PIN = 21; // ESP32 +#elif defined(ARDUINO_ARCH_AVR) +constexpr uint8_t ADB_PIN = 2; // Arduino Leonardo/Micro +#endif + +constexpr uint16_t POLL_INTERVAL = 10; // 10ms (100Hz) + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// États +bool keyboardConnected = false; +bool mouseConnected = false; + +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(F("ADB2USB - Multi-Platform Example")); + + // Initialisation du bus ADB + adb.init(); + + // Initialisation USB HID +#ifdef ARDUINO_ARCH_STM32 + USBHID_begin(true, true); +#elif defined(ARDUINO_ARCH_ESP32) + USBHID_begin(true, true); +#elif defined(ARDUINO_ARCH_AVR) + Keyboard.begin(); + Mouse.begin(); +#endif + + // Détection des périphériques + bool error; + keyboardConnected = !devices.keyboardReadModifiers(&error); + mouseConnected = !devices.mouseReadData(&error); + + Serial.print(F("Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); +} + +void loop() { + // Gestion du clavier + if (keyboardConnected) { + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) { + keyboardConnected = false; + Serial.println(F("Clavier déconnecté")); + } else { +#ifdef ARDUINO_ARCH_AVR + // Gestion HID pour Arduino Leonardo/Micro + if (keyPress.data.key0 && !keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE) { + Keyboard.press(hidCode); + } + } + if (keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE) { + Keyboard.release(hidCode); + } + } +#else + // Gestion HID pour STM32/ESP32 + uint8_t keyboardReport[8] = {0}; + keyboardReport[2] = ADBKeymap::toHID(keyPress.data.key0); + USBHID_keyboard_report(keyboardReport); +#endif + } + } + + // Gestion de la souris + if (mouseConnected) { + bool error = false; + auto mouseData = devices.mouseReadData(&error); + if (error) { + mouseConnected = false; + Serial.println(F("Souris déconnectée")); + } else { + int8_t deltaX = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t deltaY = adbMouseConvertAxis(mouseData.data.y_offset); + bool button = mouseData.data.button; + +#ifdef ARDUINO_ARCH_AVR + // Gestion HID pour Arduino Leonardo/Micro + Mouse.move(deltaX, deltaY); + if (button) { + Mouse.press(MOUSE_LEFT); + } else { + Mouse.release(MOUSE_LEFT); + } +#else + // Gestion HID pour STM32/ESP32 + uint8_t mouseReport[4] = {0}; + mouseReport[0] = button ? 1 : 0; + mouseReport[1] = deltaX; + mouseReport[2] = deltaY; + USBHID_mouse_report(mouseReport); +#endif + } + } + + delay(POLL_INTERVAL); +} diff --git a/examples/platformio_USB_stm32_example/README.md b/examples/platformio_USB_stm32_example/README.md new file mode 100644 index 0000000..4809d9d --- /dev/null +++ b/examples/platformio_USB_stm32_example/README.md @@ -0,0 +1,129 @@ +# ADB2USB pour STM32 - Convertisseur Apple Desktop Bus vers USB + +![Version](https://img.shields.io/badge/version-1.0-blue.svg) +![Licence](https://img.shields.io/badge/licence-GPL%20v3-green.svg) +![Plateforme](https://img.shields.io/badge/plateforme-STM32-red.svg) +![Framework](https://img.shields.io/badge/framework-Arduino-brightgreen.svg) + +

+ Logo ADB2USB +

+ +> Donnez une seconde vie à vos périphériques Apple vintage en les connectant à des ordinateurs modernes ! + +## 📋 Table des matières + +- [À propos du projet](#à-propos) +- [Fonctionnalités](#fonctionnalités) +- [Matériel requis](#matériel-requis) +- [Schéma de branchement](#branchements) +- [Installation](#installation) +- [Utilisation](#utilisation) +- [Personnalisation](#personnalisation) +- [Débogage](#débogage) +- [Contribution](#contribution) +- [Licence](#licence) +- [Contact](#contact) + +## ℹ️ À propos + +Cette implémentation permet de connecter des anciens claviers et souris Apple utilisant le bus ADB à un ordinateur moderne via USB. Le microcontrôleur STM32 agit comme un pont de conversion, interprétant les signaux ADB et les transformant en signaux USB HID. + +## ✨ Fonctionnalités + +- ⌨️ Prise en charge des claviers ADB (touches et modificateurs) +- 🖱️ Prise en charge des souris ADB (déplacements et boutons) +- 🔍 Détection automatique des périphériques ADB +- 🔄 Reconnexion automatique des périphériques déconnectés +- 💡 Synchronisation des LED (Verr. Maj, etc.) entre USB et ADB +- 📦 Exemple complet pour PlatformIO + +## 🔌 Matériel requis + +- Carte de développement STM32 compatible avec Arduino (par exemple, STM32 Blue Pill) +- Périphériques ADB (clavier et/ou souris Apple) +- Connecteur ADB (Mini-DIN 4 broches) +- Résistance pull-up de 4.7kΩ à 10kΩ pour la ligne de données ADB +- Câble USB pour connecter la carte STM32 à l'ordinateur + +## 🔧 Branchements + +``` +ADB Mini-DIN 4 broches: + - Pin 1 (ADB Data) → STM32 Pin PB4 (avec résistance pull-up 4.7kΩ à 10kΩ vers 5V) + - Pin 2 (Power SW) → Non connecté + - Pin 3 (5V) → 5V + - Pin 4 (GND) → GND +``` + +
+ Schéma de câblage + Schéma de câblage +
+ +## 🚀 Installation + +### Prérequis logiciels + +- [PlatformIO](https://platformio.org/) (installé comme extension VS Code ou en ligne de commande) +- Environnement de développement STM32 pour Arduino + +### Étapes d'installation + +1. Cloner ce dépôt + ```bash + git clone https://github.com/electron-rare/stm32-adb2usb.git + cd stm32-adb2usb + ``` + +2. Ouvrir le dossier dans PlatformIO + ```bash + code . + ``` + +3. Compiler et téléverser vers votre carte + ```bash + pio run -t upload + ``` + +## 📝 Utilisation + +Une fois le code téléversé: + +1. Connectez votre périphérique ADB à la carte STM32 +2. Connectez la carte STM32 à votre ordinateur via USB +3. Les périphériques ADB seront automatiquement détectés et fonctionneront comme des périphériques USB HID + +## ⚙️ Personnalisation + +Pour adapter ce code à votre matériel spécifique: +- Modifiez la constante `ADB_PIN` pour correspondre à votre brochage +- Ajustez `POLL_INTERVAL` selon vos besoins de réactivité + +## 🐛 Débogage + +Vous pouvez suivre le processus de détection et la connexion des périphériques via le moniteur série: + +```bash +pio device monitor -b 115200 +``` + +## 👥 Contribution + +Les contributions sont les bienvenues ! N'hésitez pas à ouvrir une issue ou une pull request. + +1. Forkez le projet +2. Créez votre branche (`git checkout -b feature/amazing-feature`) +3. Commitez vos changements (`git commit -m 'Add some amazing feature'`) +4. Pushez vers la branche (`git push origin feature/amazing-feature`) +5. Ouvrez une Pull Request + +## 📄 Licence + +Ce projet est sous licence GNU GPL v3. Voir le fichier `LICENSE` pour plus de détails. + +## 📧 Contact + +Clément SAILLANT - [@lelectron_rare](https://twitter.com/lelectron_rare) - contact@lelectron-rare.fr + +Lien du projet: [https://github.com/electron-rare/stm32-adb2usb](https://github.com/electron-rare/stm32-adb2usb) diff --git a/examples/platformio_USB_stm32_example/platformio.ini b/examples/platformio_USB_stm32_example/platformio.ini new file mode 100644 index 0000000..79d6fc7 --- /dev/null +++ b/examples/platformio_USB_stm32_example/platformio.ini @@ -0,0 +1,25 @@ +[env:bluepill_f103c8] +platform = ststm32 +board = bluepill_f103c8 +framework = arduino +lib_deps = + electronrare/ADB @ ^1.0.0 + electronrare/USBHID @ ^1.0.0 +upload_protocol = stlink +monitor_speed = 115200 +build_flags = + -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC + -D USBCON + -D USB_MANUFACTURER="ElectronRare" + -D USB_PRODUCT="ADB2USB HID" + +[env:stm32] +platform = ststm32 +board = bluepill_f103c8 +framework = arduino +upload_protocol = stlink +monitor_speed = 115200 + +lib_deps = + ADB + USBHID diff --git a/examples/platformio_USB_stm32_example/src/main.cpp b/examples/platformio_USB_stm32_example/src/main.cpp new file mode 100644 index 0000000..0feffb4 --- /dev/null +++ b/examples/platformio_USB_stm32_example/src/main.cpp @@ -0,0 +1,257 @@ +/** + * @file main.cpp + * @brief Exemple PlatformIO pour STM32 avec la bibliothèque ADB + * + * Cet exemple montre comment utiliser la bibliothèque ADB dans un projet PlatformIO + * pour convertir un périphérique ADB en USB HID avec un STM32. + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include +#include +#include + +// Configuration pour STM32 +constexpr uint8_t ADB_PIN = PB4; // Pin de données ADB +constexpr uint16_t POLL_INTERVAL = 10; // 10ms (100Hz) + +// Variables globales +ADB adb(ADB_PIN); +ADBDevices devices(adb); +ADBUtils utils(devices); + +// États +bool keyboardConnected = false; +bool mouseConnected = false; +uint8_t keyboardReport[8] = {0}; // Modificateurs + touches +uint8_t mouseReport[4] = {0}; // Bouton, X, Y, Wheel + +// Variables pour le suivi des mouvements de souris +int16_t accumulatedX = 0; +int16_t accumulatedY = 0; + +void setup() { + // Initialisation de la communication série + Serial.begin(115200); + delay(1000); + + Serial.println(F("ADB2USB pour STM32 - PlatformIO")); + Serial.println(F("Bibliothèque ADB multiplateforme")); + + // Initialisation du bus ADB + adb.init(); + + // Initialisation USB HID (clavier + souris) + USBHID_begin(true, true); + + // Détection des périphériques ADB + bool error; + + // Test du clavier + auto kbData = devices.keyboardReadModifiers(&error); + keyboardConnected = !error; + + // Test de la souris + error = false; + auto mouseData = devices.mouseReadData(&error); + mouseConnected = !error; + + Serial.print(F("Périphériques détectés - Clavier: ")); + Serial.print(keyboardConnected ? F("Oui") : F("Non")); + Serial.print(F(", Souris: ")); + Serial.println(mouseConnected ? F("Oui") : F("Non")); + + Serial.println(F("Conversion ADB->USB active")); +} + +void handleKeyboard() { + if (!keyboardConnected) return; + + bool error = false; + static uint8_t lastModifiers = 0; + static uint8_t lastKeys[6] = {0}; + bool reportChanged = false; + + // Lecture des touches + auto keyPress = devices.keyboardReadKeyPress(&error); + if (error) { + keyboardConnected = false; + Serial.println(F("Clavier ADB déconnecté")); + return; + } + + // Lecture des modificateurs + auto modifiers = devices.keyboardReadModifiers(&error); + if (error) return; + + // Construction du rapport clavier USB HID + uint8_t modByte = 0; + if (modifiers.data.shift) modByte |= KEY_MOD_LSHIFT; + if (modifiers.data.control) modByte |= KEY_MOD_LCTRL; + if (modifiers.data.option) modByte |= KEY_MOD_LALT; + if (modifiers.data.command) modByte |= KEY_MOD_LMETA; + + if (modByte != lastModifiers) { + lastModifiers = modByte; + reportChanged = true; + } + + // Effacement des touches + memset(&keyboardReport[2], 0, 6); + + // Ajout des touches + uint8_t keyIdx = 0; + + if (keyPress.data.key0 && !keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key0)) { + keyboardReport[2 + keyIdx++] = hidCode; + } + } + + if (keyPress.data.key1 && !keyPress.data.released1) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key1); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key1)) { + keyboardReport[2 + keyIdx++] = hidCode; + } + } + + // Vérification des changements + for (uint8_t i = 0; i < 6; i++) { + if (keyboardReport[2 + i] != lastKeys[i]) { + lastKeys[i] = keyboardReport[2 + i]; + reportChanged = true; + } + } + + // Envoi du rapport si nécessaire + if (reportChanged) { + keyboardReport[0] = modByte; + keyboardReport[1] = 0; // Reserved byte + USBHID_keyboard_report(keyboardReport); + } +} + +void handleMouse() { + if (!mouseConnected) return; + + bool error = false; + static bool lastButton = false; + + // Lecture des données de la souris + auto mouseData = devices.mouseReadData(&error); + if (error) { + mouseConnected = false; + Serial.println(F("Souris ADB déconnectée")); + return; + } + + // Conversion des valeurs + int8_t deltaX = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t deltaY = adbMouseConvertAxis(mouseData.data.y_offset); + bool button = mouseData.data.button; + + // Accumulation des petits mouvements + accumulatedX += deltaX; + accumulatedY += deltaY; + + // Envoi du rapport souris si mouvement ou changement de bouton + if (accumulatedX != 0 || accumulatedY != 0 || button != lastButton) { + // Limitation des valeurs + int8_t reportX = (accumulatedX > 127) ? 127 : ((accumulatedX < -127) ? -127 : accumulatedX); + int8_t reportY = (accumulatedY > 127) ? 127 : ((accumulatedY < -127) ? -127 : accumulatedY); + + // Construction du rapport + mouseReport[0] = button ? 1 : 0; // Bouton gauche + mouseReport[1] = reportX; + mouseReport[2] = reportY; + mouseReport[3] = 0; // Molette + + // Envoi du rapport + USBHID_mouse_report(mouseReport); + + // Mise à jour de l'état + lastButton = button; + + // Réinitialisation des accumulateurs après envoi + accumulatedX -= reportX; + accumulatedY -= reportY; + } +} + +void updateLEDs() { + // Synchronisation des LEDs USB avec le clavier ADB + static uint8_t lastLEDs = 0; + uint8_t currentLEDs = USBHID_get_status(); + + if (currentLEDs != lastLEDs) { + lastLEDs = currentLEDs; + + bool numLock = (currentLEDs & 0x01); + bool capsLock = (currentLEDs & 0x02); + bool scrollLock = (currentLEDs & 0x04); + + if (keyboardConnected) { + devices.keyboardWriteLEDs(scrollLock, capsLock, numLock); + } + } +} + +void reconnectDevices() { + static uint32_t lastReconnectAttempt = 0; + uint32_t now = millis(); + + // Tentative de reconnexion toutes les 3 secondes + if (now - lastReconnectAttempt > 3000) { + lastReconnectAttempt = now; + + bool shouldReconnect = false; + + if (!keyboardConnected) { + bool error = false; + devices.keyboardReadModifiers(&error); + if (!error) { + keyboardConnected = true; + Serial.println(F("Clavier ADB reconnecté")); + shouldReconnect = true; + } + } + + if (!mouseConnected) { + bool error = false; + devices.mouseReadData(&error); + if (!error) { + mouseConnected = true; + Serial.println(F("Souris ADB reconnectée")); + shouldReconnect = true; + } + } + + if (shouldReconnect) { + Serial.println(F("Périphériques ADB actifs:")); + Serial.print(F("- Clavier: ")); + Serial.println(keyboardConnected ? F("Connecté") : F("Déconnecté")); + Serial.print(F("- Souris: ")); + Serial.println(mouseConnected ? F("Connectée") : F("Déconnectée")); + } + } +} + +void loop() { + // Lecture et conversion des périphériques ADB + handleKeyboard(); + handleMouse(); + updateLEDs(); + + // Reconnexion si nécessaire + if (!keyboardConnected || !mouseConnected) { + reconnectDevices(); + } + + // Délai de polling optimal + delay(POLL_INTERVAL); +} diff --git a/examples/usb_hid_stm32.cpp b/examples/usb_hid_stm32.cpp new file mode 100644 index 0000000..9940431 --- /dev/null +++ b/examples/usb_hid_stm32.cpp @@ -0,0 +1,252 @@ +/** + * @file usb_hid_stm32.cpp + * @brief Exemple de conversion ADB vers USB HID pour STM32 + * + * Cet exemple montre comment convertir un clavier et une souris ADB en un périphérique + * USB HID sur les plateformes STM32. Il permet d'utiliser des périphériques Apple vintage + * sur des ordinateurs modernes via USB. + * + * @note Cet exemple nécessite un STM32 avec prise en charge USB (STM32F1, STM32F4, etc.) + * + * @author Clément SAILLANT - L'électron rare + * @copyright Copyright (C) 2025 Clément SAILLANT + * @license GNU GPL v3 + */ + +#include +#include "adb.h" +#include "USBHID.h" // Bibliothèque STM32 USB HID + +// Configuration des broches +constexpr uint8_t ADB_PIN = PB4; // Broche de données ADB + +// Configuration des constantes +constexpr uint16_t POLL_INTERVAL = 10; // Fréquence d'interrogation en ms (10ms = 100Hz) +constexpr uint8_t MAX_KEYS = 6; // Nombre maximum de touches dans un rapport HID + +// Initialisation des objets +ADB adb(ADB_PIN); +ADBDevices devices(adb); + +// Buffers pour les rapports HID +uint8_t keyboardReport[8] = {0}; // Modificateurs (1) + réservé (1) + touches (6) +uint8_t mouseReport[4] = {0}; // Boutons (1) + X (1) + Y (1) + molette (1) + +// État précédent des périphériques +uint8_t lastKeyboardKeys[MAX_KEYS] = {0}; +uint8_t lastModifiers = 0; +int16_t mouseAccumulatedX = 0; +int16_t mouseAccumulatedY = 0; +bool lastButton = false; + +// Indicateurs de présence des périphériques +bool keyboardPresent = false; +bool mousePresent = false; + +/** + * @brief Met à jour les LEDs du clavier ADB en fonction de l'état USB + */ +void updateKeyboardLEDs() { + static uint8_t lastLEDState = 0; + uint8_t currentLEDs = USBHID_get_status(); + + if (currentLEDs != lastLEDState) { + lastLEDState = currentLEDs; + + bool numLock = (currentLEDs & 0x01) != 0; + bool capsLock = (currentLEDs & 0x02) != 0; + bool scrollLock = (currentLEDs & 0x04) != 0; + + // Mise à jour des LEDs sur le clavier ADB + if (keyboardPresent) { + devices.keyboardWriteLEDs(scrollLock, capsLock, numLock); + } + + Serial.print(F("LEDs mises à jour: ")); + Serial.print(capsLock ? F("CapsLock ") : F("")); + Serial.print(numLock ? F("NumLock ") : F("")); + Serial.println(scrollLock ? F("ScrollLock") : F("")); + } +} + +/** + * @brief Détecte et initialise les périphériques ADB + */ +void detectADBDevices() { + bool error = false; + + // Essai de lecture des modificateurs du clavier + devices.keyboardReadModifiers(&error); + keyboardPresent = !error; + + // Essai de lecture de la souris + error = false; + devices.mouseReadData(&error); + mousePresent = !error; + + Serial.print(F("Détection ADB: Clavier: ")); + Serial.print(keyboardPresent ? F("Oui") : F("Non")); + Serial.print(F(", Souris: ")); + Serial.println(mousePresent ? F("Oui") : F("Non")); +} + +/** + * @brief Lit les données du clavier ADB et les convertit en rapport USB HID + */ +void handleKeyboard() { + if (!keyboardPresent) return; + + bool error = false; + auto keyPress = devices.keyboardReadKeyPress(&error); + + if (error) { + keyboardPresent = false; + Serial.println(F("Erreur: Clavier ADB déconnecté")); + return; + } + + // Mise à jour des modificateurs (registre 2) + auto modifiers = devices.keyboardReadModifiers(&error); + if (error) return; + + // Calcul du nouvel état des modificateurs + uint8_t newModifiers = 0; + if (modifiers.data.shift) newModifiers |= KEY_MOD_LSHIFT; + if (modifiers.data.control) newModifiers |= KEY_MOD_LCTRL; + if (modifiers.data.option) newModifiers |= KEY_MOD_LALT; + if (modifiers.data.command) newModifiers |= KEY_MOD_LMETA; + + // Construction du rapport clavier + keyboardReport[0] = newModifiers; // Octet des modificateurs + + // Effacer les touches + memset(&keyboardReport[2], 0, MAX_KEYS); + + // Ajouter les touches pressées + uint8_t keyIndex = 0; + + // Gestion de la touche 0 + if (keyPress.data.key0 != 0 && !keyPress.data.released0) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key0); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key0)) { + keyboardReport[2 + keyIndex++] = hidCode; + } + } + + // Gestion de la touche 1 + if (keyPress.data.key1 != 0 && !keyPress.data.released1) { + uint8_t hidCode = ADBKeymap::toHID(keyPress.data.key1); + if (hidCode != KEY_NONE && !ADBKeymap::isModifier(keyPress.data.key1)) { + keyboardReport[2 + keyIndex++] = hidCode; + } + } + + // Envoi du rapport uniquement s'il a changé + bool changed = false; + if (newModifiers != lastModifiers) { + changed = true; + lastModifiers = newModifiers; + } + + for (uint8_t i = 0; i < MAX_KEYS; i++) { + if (keyboardReport[2 + i] != lastKeyboardKeys[i]) { + changed = true; + lastKeyboardKeys[i] = keyboardReport[2 + i]; + } + } + + if (changed) { + USBHID_keyboard_report(keyboardReport); + } +} + +/** + * @brief Lit les données de la souris ADB et les convertit en rapport USB HID + */ +void handleMouse() { + if (!mousePresent) return; + + bool error = false; + auto mouseData = devices.mouseReadData(&error); + + if (error) { + mousePresent = false; + Serial.println(F("Erreur: Souris ADB déconnectée")); + return; + } + + // Récupération des données souris + int8_t deltaX = adbMouseConvertAxis(mouseData.data.x_offset); + int8_t deltaY = adbMouseConvertAxis(mouseData.data.y_offset); + bool buttonPressed = mouseData.data.button; + + // Cumul des petits mouvements pour éviter la perte de mouvements lents + mouseAccumulatedX += deltaX; + mouseAccumulatedY += deltaY; + + // Création du rapport souris seulement s'il y a un mouvement ou un changement d'état du bouton + if (mouseAccumulatedX != 0 || mouseAccumulatedY != 0 || buttonPressed != lastButton) { + mouseReport[0] = buttonPressed ? 1 : 0; // Bouton gauche + + // Limitation des valeurs pour tenir dans un int8_t + if (mouseAccumulatedX > 127) mouseAccumulatedX = 127; + if (mouseAccumulatedX < -127) mouseAccumulatedX = -127; + if (mouseAccumulatedY > 127) mouseAccumulatedY = 127; + if (mouseAccumulatedY < -127) mouseAccumulatedY = -127; + + mouseReport[1] = (int8_t)mouseAccumulatedX; + mouseReport[2] = (int8_t)mouseAccumulatedY; + mouseReport[3] = 0; // Pas de défilement + + // Envoi du rapport souris + USBHID_mouse_report(mouseReport); + + // Réinitialisation des accumulateurs + mouseAccumulatedX = 0; + mouseAccumulatedY = 0; + lastButton = buttonPressed; + } +} + +void setup() { + // Initialisation de la communication série pour le débogage + Serial.begin(115200); + while (!Serial && millis() < 3000); // Attente avec timeout + + Serial.println(F("=== Convertisseur ADB vers USB HID pour STM32 ===")); + Serial.println(F("Initialisation du bus ADB...")); + + // Initialisation du bus ADB + adb.init(); + + // Initialisation USB HID + USBHID_begin(true, true); // Activer clavier + souris + + // Détection des périphériques + detectADBDevices(); + + Serial.println(F("Initialisation terminée")); + Serial.println(F("Le périphérique devrait maintenant être reconnu comme un clavier/souris USB")); +} + +void loop() { + // Lecture des périphériques ADB et envoi des rapports USB HID + handleKeyboard(); + handleMouse(); + + // Mise à jour des LEDs du clavier ADB en fonction de l'état du clavier USB + updateKeyboardLEDs(); + + // Si un périphérique a été déconnecté, tentative de reconnexion + static uint32_t lastReconnectTime = 0; + if ((!keyboardPresent || !mousePresent) && (millis() - lastReconnectTime > 1000)) { + if (!keyboardPresent || !mousePresent) { + Serial.println(F("Tentative de reconnexion des périphériques ADB...")); + detectADBDevices(); + lastReconnectTime = millis(); + } + } + + // Pause entre les lectures pour ne pas surcharger le bus + delay(POLL_INTERVAL); +} diff --git a/logo-specifications.md b/logo-specifications.md new file mode 100644 index 0000000..6c0198e --- /dev/null +++ b/logo-specifications.md @@ -0,0 +1,44 @@ +# Spécifications du logo ADB2USB + +## Concept +Un logo représentant la conversion des périphériques Apple Desktop Bus (ADB) vers USB, combinant des éléments vintage Apple et la modernité. + +## Éléments principaux +1. **Symbole ADB**: Représentation stylisée du connecteur Mini-DIN 4 broches ADB +2. **Flèche de transition**: Indiquant la conversion d'une technologie à l'autre +3. **Symbole USB**: Icône USB standard ou connecteur USB-A stylisé +4. **Carte STM32**: Petite silhouette d'une carte STM32 (optionnelle) + +## Palette de couleurs +- **ADB (côté gauche)**: Couleurs vintage Apple + - Beige: #E0D7C5 (couleur des premiers Macintosh) + - Rainbow Apple: Utiliser des éléments du logo Apple arc-en-ciel +- **USB (côté droit)**: Couleurs modernes + - Bleu: #3B7BBF (bleu USB standard) + - Blanc: #FFFFFF +- **Accent**: Rouge STM32 (#EE3424) pour la partie microcontrôleur + +## Style et composition +- Style épuré et légèrement rétro +- Disposition horizontale avec ADB à gauche, USB à droite +- Polices suggérées: + - "Chicago" ou "Geneva" (polices vintage Apple) pour la partie ADB + - "Source Sans Pro" ou "Helvetica Neue" pour la partie USB + +## Variantes à créer +1. **Logo complet**: Tous les éléments avec texte "ADB2USB" +2. **Icône seule**: Version sans texte pour favicon ou petits affichages +3. **Version monochrome**: Pour impression ou situations à contraste limité + +## Dimensions recommandées +- Logo principal: 2000x800px (ratio 2.5:1) +- Icône: 512x512px (ratio 1:1) +- Prévoir des versions pour GitHub, Twitter et autres plateformes sociales + +## Exemples d'inspiration +- Logo Apple rétro (1977-1998) +- Logos d'interface HID standards +- Design minimaliste des logos de projets open source + +## Production +Créer le logo en format vectoriel (SVG) pour permettre un redimensionnement sans perte de qualité, puis exporter en PNG pour différentes utilisations. diff --git a/platformio_example/library.json b/platformio_example/library.json new file mode 100644 index 0000000..804cc58 --- /dev/null +++ b/platformio_example/library.json @@ -0,0 +1,23 @@ +{ + "name": "ADB", + "version": "1.0.0", + "description": "Une bibliothèque multiplateforme pour le protocole Apple Desktop Bus (ADB)", + "keywords": "adb, apple, vintage, usb, hid, keyboard, mouse", + "repository": { + "type": "git", + "url": "https://github.com/electronrare/stm32-adb2usb" + }, + "authors": [ + { + "name": "Clément SAILLANT", + "email": "contact@electronrare.com", + "url": "https://electronrare.com", + "maintainer": true + } + ], + "license": "GPL-3.0-or-later", + "homepage": "https://github.com/electronrare/stm32-adb2usb", + "dependencies": {}, + "frameworks": "arduino", + "platforms": ["ststm32", "espressif32", "atmelavr", "teensy"] +}