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"] +}