first commit

This commit is contained in:
Clément SAILLANT
2025-03-26 15:49:24 +01:00
parent b6cf40f602
commit fb5f2b5a20
32 changed files with 3428 additions and 0 deletions
+277
View File
@@ -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<uint16_t>(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<adb_register3> reg3 = {0};
adb_data<adb_register3> 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<adb_kb_modifiers> ADBDevices::keyboardReadModifiers(bool* error) {
adb_data<adb_kb_modifiers> 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<adb_kb_keypress> ADBDevices::keyboardReadKeyPress(bool* error) {
adb_data<adb_kb_keypress> 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<adb_kb_modifiers> 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<adb_mouse_data> ADBDevices::mouseReadData(bool* error) {
adb_data<adb_mouse_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<adb_register3> ADBDevices::deviceReadRegister3(uint8_t addr, bool* error) {
adb_data<adb_register3> 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(&reg3.raw, 16);
return reg3;
}
bool ADBDevices::deviceUpdateRegister3(uint8_t addr, adb_data<adb_register3> newReg3, uint16_t mask, bool* error) {
// Lecture de la configuration actuelle
adb_data<adb_register3> 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);
}
+235
View File
@@ -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 <Arduino.h>
#include <cstdint>
#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 <typename T>
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<adb_kb_modifiers> 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<adb_kb_keypress> 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<adb_mouse_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<adb_register3> 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<adb_register3> 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<int8_t>(value << 1);
}
#endif // ADB_MAIN_h
+19
View File
@@ -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
+60
View File
@@ -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 <cstdint>
/**
* 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
+27
View File
@@ -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
};
+54
View File
@@ -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 <cstdint>
#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
+81
View File
@@ -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
+101
View File
@@ -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 <Arduino.h>
#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
+274
View File
@@ -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
+177
View File
@@ -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 <Arduino.h>
#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.
+73
View File
@@ -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 <Arduino.h>
#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);
}
+91
View File
@@ -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 <Arduino.h>
#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);
}
+167
View File
@@ -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 <Arduino.h>
#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<adb_register3> reg3 = {0};
adb.writeCommand(ADBProtocol::CMD_TALK | ADBProtocol::ADDRESS(addr) | ADBProtocol::REGISTER(3));
adb.waitTLT(true);
bool success = adb.readDataPacket(&reg3.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);
}
+144
View File
@@ -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 <Arduino.h>
#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);
}
+55
View File
@@ -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 <Arduino.h>
#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);
}
@@ -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).
<p align="center">
<img src="https://placeholder-image-url.com/adb2ble_adapter.jpg" alt="ADB2BLE Adapter" width="500">
</p>
## 📋 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
@@ -0,0 +1,7 @@
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
electronrare/ADB
monitor_speed = 115200
@@ -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 <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#include <BLEDevice.h>
#include <BLEHIDDevice.h>
#include <HIDTypes.h>
// 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);
}
@@ -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
@@ -0,0 +1,55 @@
#include <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#include <USBHID.h>
// 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);
}
@@ -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
@@ -0,0 +1,55 @@
#include <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#include <USBHID.h>
// 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);
}
@@ -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
@@ -0,0 +1,88 @@
#include <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#include <HID-Project.h> // 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);
}
@@ -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
@@ -0,0 +1,127 @@
#include <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#ifdef ARDUINO_ARCH_STM32
#include <USBHID.h> // STM32 USB HID
#elif defined(ARDUINO_ARCH_ESP32)
#include <USBHID.h> // ESP32 USB HID
#elif defined(ARDUINO_ARCH_AVR)
#include <HID-Project.h> // 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);
}
@@ -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)
<p align="center">
<img src="https://raw.githubusercontent.com/electron-rare/stm32-adb2usb/main/docs/assets/adb2usb-logo.png" alt="Logo ADB2USB" width="300" />
</p>
> 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
```
<details>
<summary>Schéma de câblage</summary>
<img src="https://raw.githubusercontent.com/electron-rare/stm32-adb2usb/main/docs/assets/wiring.png" alt="Schéma de câblage" width="600" />
</details>
## 🚀 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)
@@ -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
@@ -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 <Arduino.h>
#include <ADB.h>
#include <ADBUtils.h>
#include <USBHID.h>
// 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);
}
+252
View File
@@ -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 <Arduino.h>
#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);
}
+44
View File
@@ -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.
+23
View File
@@ -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"]
}