Compare commits

..

No commits in common. "master" and "v0.1.0" have entirely different histories.

6 changed files with 0 additions and 1406 deletions

Binary file not shown.

View file

@ -1,112 +0,0 @@
# Modèle 3D — Companion Cube
## Architecture : Pièces Modulaires Séparées
```
ASSEMBLAGE
==========
1. Frame (squelette)
↓ vissé dessus
2. Battery Cradle + PCB Central Mount
↓ électronique montée
3. PCB Carriers (x6) vissés sur plots du Frame
↓ PCBs montés + câblés
4. Face Covers (x6) vissées sur Carriers
└─ Joint O-ring entre Carrier et Cover
```
## Fichiers
| Fichier | Description |
|---------|-------------|
| `cube-config.scad` | **Paramètres** — modifier ici pour ajuster dimensions |
| `cube-parts.scad` | **Pièces** — définition des 10 pièces à imprimer |
| `cube-assembly.scad` | **Assemblage** — vue complète, ouvrir dans OpenSCAD |
## 10 Pièces à Imprimer
| # | Pièce | Qté | Matériau | Rôle |
|---|-------|-----|----------|------|
| 1 | `frame` | 1 | PLA/PETG opaque gris | Squelette principal |
| 2 | `battery_cradle` | 1 | PLA/PETG | Support batterie LiPo |
| 3 | `pcb_central_mount` | 1 | PLA/PETG | Support PCB ESP32-S3 |
| 4 | `pcb_carrier_standard` | 4 | PLA/PETG | Supports PCB faces latérales |
| 5 | `pcb_carrier_nfc` | 1 | PLA/PETG | Support PCB face dessus (NFC) |
| 6 | `pcb_carrier_qi` | 1 | PLA/PETG | Support PCB face dessous (Qi) |
| 7 | `face_cover_standard` | 4 | PETG translucide | Coques extérieures latérales |
| 8 | `face_cover_nfc` | 1 | PETG translucide | Coque extérieure dessus |
| 9 | `face_cover_qi` | 1 | PETG translucide | Coque extérieure dessous |
**Total : 15 pièces** (1+1+1 + 4+1+1 + 4+1+1)
## Utilisation
1. **Installer OpenSCAD** : https://openscad.org/
2. **Ouvrir** `cube-assembly.scad`
3. **Prévisualiser** : F5 (rapide) ou F6 (complet)
### Contrôles de visualisation
Dans `cube-assembly.scad`, modifier les variables :
```openscad
SHOW_FRAME = true; // Squelette
SHOW_INTERNALS = true; // Battery Cradle + PCB Mount
SHOW_PCB_CARRIERS = true; // 6 supports PCB
SHOW_FACE_COVERS = true; // 6 coques extérieures
SHOW_ELECTRONICS = true; // Batterie + PCB (visuel)
SHOW_SECTION = false; // Vue en coupe
EXPLODE = 0; // 0 à 1 (vue éclatée)
```
### Export STL pour impression
1. Éditer `cube-assembly.scad`
2. **Décommenter** le module souhaité (fin du fichier)
3. **Commenter** `assembly()`
4. F6 → File → Export as STL
Exemple :
```openscad
// assembly(); // ← commenter
frame(); // ← décommenter
```
## Séquence d'Assemblage
### Étape 1 : Frame + Internals
1. Imprimer `frame`, `battery_cradle`, `pcb_central_mount`
2. Visser les supports au frame (vis M2.5 ou M3)
3. Monter batterie + PCB central + câblage
### Étape 2 : PCB Carriers
1. Imprimer les 6 carriers (4 standard + 1 NFC + 1 Qi)
2. Monter les PCBs Anneau + Bouchon sur chaque carrier
3. Visser chaque carrier sur les 4 plots du frame (M3)
4. Câbler JST depuis le PCB central vers chaque carrier
### Étape 3 : Face Covers
1. Imprimer les 6 covers (PETG translucide)
2. Insérer joint O-ring (Ø cordon 2mm) dans la gorge
3. Visser chaque cover sur son carrier (M3)
4. Serrer uniformément les 4 vis
## Paramètres Clés
| Paramètre | Valeur | Impact |
|-----------|--------|--------|
| `CUBE_SIZE` | 120 mm | Taille globale du cube |
| `CORNER_RADIUS` | 6 mm | Arrondi des coins (Portal style) |
| `FACE_MEMBRANE_T` | 1.0 mm | Membrane tactile (sensibilité) |
| `SEAL_CORD_DIAM` | 2.0 mm | Diamètre cordon O-ring |
| `PRINT_TOLERANCE` | 0.2 mm | Ajuster selon votre imprimante |
## Matériel nécessaire
- **Vis M3 x 8mm** : 24 (fixation carriers sur frame)
- **Vis M3 x 6mm** : 24 (fixation covers sur carriers)
- **Vis M2.5 x 6mm** : 8 (battery cradle + pcb mount)
- **Joint O-ring Ø2mm** : 6m (1m par face, gorge continue)
- **Inserts filetés M3** (optionnel) : 48

View file

@ -1,271 +0,0 @@
// ============================================================================
// Companion Cube Modèle 3D Complet
// Version : 0.3.0
// Architecture : Pièces modulaires séparées
// ============================================================================
//
// STRUCTURE (10 pièces à imprimer) :
// 1. Frame (squelette) Structure principale avec plots de fixation
// 2. Battery Cradle Support batterie (vissé au frame)
// 3. PCB Central Mount Support PCB central (vissé au frame)
// 4-9. PCB Carriers (x6) Support PCB pour chaque face (vissé sur plots)
// - Standard (x4)
// - NFC (x1, dessus)
// - Qi (x1, dessous)
// 10-15. Face Covers (x6) Coques extérieures (étanchéité + look Portal)
// - Standard (x4)
// - NFC (x1)
// - Qi (x1)
//
// ASSEMBLAGE :
// 1. Visser Battery Cradle + PCB Central Mount au Frame
// 2. Monter l'électronique (batterie, PCB central, câbles)
// 3. Visser chaque PCB Carrier sur les plots du Frame
// 4. Monter les PCBs (Anneau + Bouchon) sur chaque Carrier
// 5. Câbler JST depuis le PCB central vers chaque Carrier
// 6. Visser les Face Covers sur les Carriers (joint O-ring entre)
//
// UTILISATION :
// - Variables `SHOW_*` pour afficher/masquer les pièces
// - Variable `EXPLODE` pour la vue éclatée (0 à 1)
// - Modules individuels exportables en STL
//
// ============================================================================
include <cube-config.scad>
include <cube-parts.scad>
// --- Contrôle de l'affichage ------------------------------------------------
SHOW_FRAME = true; // Squelette principal
SHOW_INTERNALS = true; // Battery Cradle + PCB Central Mount
SHOW_PCB_CARRIERS = true; // 6 supports PCB
SHOW_FACE_COVERS = true; // 6 coques extérieures
SHOW_ELECTRONICS = true; // Batterie + PCB (visualisation)
SHOW_SECTION = false; // Coupe en section
EXPLODE = 1; // 0 = assemblé, 1 = vue éclatée (progressif)
// Distance d'éclatement
EXPLODE_DIST = 60 * EXPLODE;
// ============================================================================
// VISUALISATION ÉLECTRONIQUE (Ne pas imprimer)
// ============================================================================
module electronics() {
// --- Batterie LiPo ---
color(COLOR_BATTERY)
translate([0, 0, -10])
cube([BATT_LENGTH, BATT_WIDTH, BATT_HEIGHT], center=true);
// --- PCB Central ---
color(COLOR_PCB)
translate([0, 0, 8 + 5])
cube([CENTRAL_PCB_LENGTH, CENTRAL_PCB_WIDTH, CENTRAL_PCB_HEIGHT], center=true);
// --- ESP32-S3 module ---
color([0.15, 0.15, 0.15])
translate([0, 0, 8 + 5 + CENTRAL_PCB_HEIGHT/2 + 1.5])
cube([18, 18, 3], center=true);
// --- TP4056 ---
color(COLOR_PCB)
translate([20, 0, 8 + 5 + CENTRAL_PCB_HEIGHT/2 + 1])
cube([10, 8, 2], center=true);
}
// PCB Anneau (visualisation)
module pcb_ring() {
color(COLOR_PCB)
difference() {
cylinder(d=LED_RING_DIAM_EXT, h=LED_RING_HEIGHT);
translate([0, 0, -0.5])
cylinder(d=LED_RING_DIAM_INT, h=LED_RING_HEIGHT + 1);
}
// LEDs WS2812B
color(COLOR_LED)
for (i = [0:LED_COUNT-1]) {
angle = i * 360 / LED_COUNT;
r = (LED_RING_DIAM_EXT + LED_RING_DIAM_INT) / 4;
translate([r * cos(angle), r * sin(angle), LED_RING_HEIGHT])
cube([5, 5, 1.5], center=true);
}
}
// PCB Bouchon (visualisation)
module pcb_cap() {
color(COLOR_PCB)
cylinder(d=CAP_PCB_DIAM, h=CAP_PCB_HEIGHT);
// Pad capacitif
color([0.8, 0.6, 0.2])
translate([0, 0, CAP_PCB_HEIGHT])
cylinder(d=CAP_PCB_DIAM - 5, h=0.1);
}
// Module NFC PN532 (visualisation)
module nfc_module() {
color(COLOR_NFC) {
cube([NFC_PCB_SIZE, NFC_PCB_SIZE, NFC_PCB_HEIGHT], center=true);
translate([0, 0, NFC_PCB_HEIGHT/2])
cylinder(d=NFC_ANTENNA_DIAM, h=0.5);
}
}
// Bobine Qi (visualisation)
module qi_coil() {
color(COLOR_QI) {
cylinder(d=QI_COIL_DIAM, h=QI_COIL_HEIGHT);
translate([0, QI_COIL_DIAM/2 + 5, 0])
cube([QI_PCB_LENGTH, QI_PCB_WIDTH, QI_PCB_HEIGHT], center=true);
}
}
// ============================================================================
// ASSEMBLAGE PRINCIPAL
// ============================================================================
module assembly() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
// --- 1. FRAME (Squelette) ---
if (SHOW_FRAME)
color(COLOR_FRAME) frame();
// --- 2. INTERNALS (Supports internes) ---
if (SHOW_INTERNALS) {
// Battery Cradle (centré, décalé vers le bas)
color([0.4, 0.4, 0.4])
translate([0, 0, -10 - EXPLODE_DIST * 0.3])
battery_cradle();
// PCB Central Mount (centré, au-dessus de la batterie)
color([0.4, 0.4, 0.4])
translate([0, 0, 8 + EXPLODE_DIST * 0.3])
pcb_central_mount();
}
// --- 3. ÉLECTRONIQUE (Visualisation) ---
if (SHOW_ELECTRONICS)
electronics();
// --- 4-9. PCB CARRIERS (6 supports PCB) ---
if (SHOW_PCB_CARRIERS) {
carrier_offset = 10; // Distance depuis la corniche du frame
// Carrier Dessus (+Z) NFC
color([0.5, 0.5, 0.5])
translate([0, 0, half - edge_d + carrier_offset + EXPLODE_DIST * 0.7])
pcb_carrier_nfc();
// Carrier Dessous (-Z) Qi
color([0.5, 0.5, 0.5])
translate([0, 0, -(half - edge_d + carrier_offset) - EXPLODE_DIST * 0.7])
rotate([180, 0, 0])
pcb_carrier_qi();
// Carrier Avant (-Y)
color([0.5, 0.5, 0.5])
translate([0, -(half - edge_d + carrier_offset) - EXPLODE_DIST * 0.7, 0])
rotate([90, 0, 0])
pcb_carrier_standard();
// Carrier Arrière (+Y)
color([0.5, 0.5, 0.5])
translate([0, (half - edge_d + carrier_offset) + EXPLODE_DIST * 0.7, 0])
rotate([-90, 0, 0])
pcb_carrier_standard();
// Carrier Gauche (-X)
color([0.5, 0.5, 0.5])
translate([-(half - edge_d + carrier_offset) - EXPLODE_DIST * 0.7, 0, 0])
rotate([0, -90, 0])
pcb_carrier_standard();
// Carrier Droite (+X)
color([0.5, 0.5, 0.5])
translate([(half - edge_d + carrier_offset) + EXPLODE_DIST * 0.7, 0, 0])
rotate([0, 90, 0])
pcb_carrier_standard();
}
// --- 10-15. FACE COVERS (6 coques extérieures) ---
if (SHOW_FACE_COVERS) {
cover_offset = 16; // Distance depuis le frame (par-dessus les carriers)
// Cover Dessus (+Z) NFC
color(COLOR_FACE)
translate([0, 0, half - edge_d + cover_offset + EXPLODE_DIST])
face_cover_nfc();
// Cover Dessous (-Z) Qi
color(COLOR_FACE)
translate([0, 0, -(half - edge_d + cover_offset) - EXPLODE_DIST])
rotate([180, 0, 0])
face_cover_qi();
// Cover Avant (-Y)
color(COLOR_FACE)
translate([0, -(half - edge_d + cover_offset) - EXPLODE_DIST, 0])
rotate([90, 0, 0])
face_cover_standard();
// Cover Arrière (+Y)
color(COLOR_FACE)
translate([0, (half - edge_d + cover_offset) + EXPLODE_DIST, 0])
rotate([-90, 0, 0])
face_cover_standard();
// Cover Gauche (-X)
color(COLOR_FACE)
translate([-(half - edge_d + cover_offset) - EXPLODE_DIST, 0, 0])
rotate([0, -90, 0])
face_cover_standard();
// Cover Droite (+X)
color(COLOR_FACE)
translate([(half - edge_d + cover_offset) + EXPLODE_DIST, 0, 0])
rotate([0, 90, 0])
face_cover_standard();
}
}
// ============================================================================
// RENDU
// ============================================================================
if (SHOW_SECTION) {
difference() {
assembly();
// Coupe en section (plan YZ, moitié +X)
translate([CUBE_SIZE/2, 0, 0])
cube([CUBE_SIZE, CUBE_SIZE * 2, CUBE_SIZE * 2], center=true);
}
} else {
assembly();
}
// ============================================================================
// MODULES D'EXPORT (décommenter pour exporter individuellement en STL)
// ============================================================================
// Pour exporter une pièce : décommenter la ligne, commenter assembly(),
// puis Exporter en STL (F6 File Export STL)
// --- Pièces Structurelles ---
// frame(); // 01-frame.stl
// battery_cradle(); // 02-battery-cradle.stl
// pcb_central_mount(); // 03-pcb-central-mount.stl
// --- PCB Carriers (6 pièces) ---
// pcb_carrier_standard(); // 04-pcb-carrier-standard.stl (à imprimer x4)
// pcb_carrier_nfc(); // 05-pcb-carrier-nfc.stl (dessus)
// pcb_carrier_qi(); // 06-pcb-carrier-qi.stl (dessous)
// --- Face Covers (6 pièces) ---
// face_cover_standard(); // 07-face-cover-standard.stl (à imprimer x4)
// face_cover_nfc(); // 08-face-cover-nfc.stl (dessus)
// face_cover_qi(); // 09-face-cover-qi.stl (dessous)

View file

@ -1,89 +0,0 @@
// ============================================================================
// Companion Cube Configuration Paramétrique
// Version : 0.2.0
// ============================================================================
// Toutes les dimensions sont en millimètres.
// Modifier ce fichier pour ajuster les dimensions sans toucher au modèle.
// ============================================================================
// --- Dimensions Générales ---------------------------------------------------
CUBE_SIZE = 120; // Arête extérieure du cube (mm)
WALL_THICKNESS = 2.5; // Épaisseur des parois de la coque/face
CORNER_RADIUS = 6; // Rayon des arrondis extérieurs
CORNER_REINFORCE = 10; // Largeur des renforts de coins
// --- Squelette Interne (Frame) ----------------------------------------------
FRAME_EDGE_WIDTH = 12; // Largeur des arêtes du squelette
FRAME_EDGE_DEPTH = 12; // Profondeur des arêtes du squelette
FRAME_FILLET = 2; // Congé sur les arêtes du squelette
FRAME_MOUNT_DIAM = 3.2; // Diamètre des trous de fixation M3
FRAME_MOUNT_HEAD = 5.8; // Diamètre de la tête de vis M3
FRAME_MOUNT_DEPTH = 3; // Profondeur du lamage tête de vis
FRAME_SCREWS_PER_FACE = 4; // Nombre de vis par face
FRAME_LEDGE_WIDTH = 5; // Largeur corniche de contact (face squelette)
FRAME_LEDGE_HEIGHT = 3; // Épaisseur de la corniche plate
// --- Faces ------------------------------------------------------------------
FACE_TOTAL_DEPTH = 14; // Profondeur totale d'un module face (du bord ext.)
FACE_CENTER_DIAM = 40; // Diamètre de la zone tactile centrale
FACE_MEMBRANE_T = 1.0; // Épaisseur de la membrane tactile (centre)
SEAL_CORD_DIAM = 2.0; // Diamètre du cordon torique (joint O-ring)
FACE_SEAL_GROOVE_W = 2.8; // Largeur rainure = ~1.4 × cordon
FACE_SEAL_GROOVE_D = 1.4; // Profondeur rainure = ~0.7 × cordon
// --- LEDs (PCB Anneau Étage B) --------------------------------------------
LED_COUNT = 12; // Nombre de WS2812B par face
LED_RING_DIAM_EXT = 70; // Diamètre extérieur de l'anneau LED
LED_RING_DIAM_INT = 40; // Diamètre intérieur (ouverture d'air)
LED_RING_HEIGHT = 1.6; // Épaisseur du PCB anneau
RING_SHROUD_HEIGHT = 6; // Hauteur de la chambre de diffusion
RING_SHROUD_WALL = 1.5; // Épaisseur paroi du shroud
// --- Capteur (PCB Bouchon Étage A) ----------------------------------------
CAP_PCB_DIAM = 35; // Diamètre du PCB bouchon
CAP_PCB_HEIGHT = 1.6; // Épaisseur du PCB bouchon
CAP_CONNECTOR_H = 4; // Hauteur des connecteurs 2.54mm
// --- NFC (Face du dessus uniquement) ----------------------------------------
NFC_PCB_SIZE = 43; // Taille du module PN532 (carré)
NFC_PCB_HEIGHT = 5; // Hauteur du module PN532 avec antenne
NFC_ANTENNA_DIAM = 35; // Diamètre de l'antenne NFC
// --- Charge Qi (Face du dessous uniquement) ---------------------------------
QI_COIL_DIAM = 50; // Diamètre de la bobine Qi réceptrice
QI_COIL_HEIGHT = 3; // Épaisseur de la bobine Qi
QI_PCB_WIDTH = 30; // Largeur du PCB récepteur Qi
QI_PCB_LENGTH = 20; // Longueur du PCB récepteur Qi
QI_PCB_HEIGHT = 2; // Épaisseur du PCB Qi
// --- Batterie ---------------------------------------------------------------
BATT_LENGTH = 70; // Longueur batterie LiPo
BATT_WIDTH = 50; // Largeur batterie LiPo
BATT_HEIGHT = 10; // Épaisseur batterie LiPo (2500mAh ~)
BATT_TOLERANCE = 1; // Jeu autour de la batterie
// --- PCB Central (Motherboard) ----------------------------------------------
CENTRAL_PCB_WIDTH = 60; // Largeur du PCB central (ESP32-S3 + TP4056 + CW2015)
CENTRAL_PCB_LENGTH = 60; // Longueur du PCB central
CENTRAL_PCB_HEIGHT = 1.6; // Épaisseur du PCB
CENTRAL_COMP_H = 8; // Hauteur composants (ESP32-S3 module)
// --- Aération ---------------------------------------------------------------
AIR_HOLE_DIAM = 30; // Diamètre de l'ouverture d'air centrale (face intérieure)
// --- Tolérances d'Impression 3D -------------------------------------------
PRINT_TOLERANCE = 0.2; // Jeu général pour emboîtement
PRINT_LAYER_H = 0.2; // Hauteur de couche standard
// --- Rendu ------------------------------------------------------------------
$fn = 60; // Résolution des cercles (augmenter pour le rendu final)
// --- Couleurs pour la visualisation ----------------------------------------
COLOR_FRAME = [0.3, 0.3, 0.3]; // Gris foncé squelette
COLOR_FACE = [0.6, 0.6, 0.6, 0.85]; // Gris clair semi-transparent faces
COLOR_MEMBRANE = [0.8, 0.8, 0.8, 0.5]; // Blanc translucide membrane tactile
COLOR_PCB = [0.1, 0.5, 0.1]; // Vert PCBs
COLOR_LED = [1.0, 0.9, 0.3]; // Jaune LEDs
COLOR_BATTERY = [0.2, 0.2, 0.6]; // Bleu foncé batterie
COLOR_NFC = [0.6, 0.1, 0.1]; // Rouge module NFC
COLOR_QI = [0.8, 0.5, 0.1]; // Orange bobine Qi

View file

@ -1,572 +0,0 @@
// ============================================================================
// Companion Cube Pièces Modulaires Séparées
// Version : 0.3.0
// ============================================================================
// Toutes les pièces à imprimer, définies comme modules indépendants.
// Ce fichier sera inclus par cube-assembly.scad
// ============================================================================
include <cube-config.scad>
// ============================================================================
// UTILITAIRES GÉOMÉTRIQUES
// ============================================================================
// Cube arrondi (hull de 8 sphères aux coins)
module rounded_cube(size, r) {
hull() {
c = size/2 - r;
for (x = [-1, 1], y = [-1, 1], z = [-1, 1])
translate([x*c, y*c, z*c])
sphere(r=r);
}
}
// ============================================================================
// PIÈCE 1 : FRAME (Squelette Principal)
// ============================================================================
// Structure porteuse avec plots de fixation pour les PCB Carriers.
// 4 plots M3 par face (24 total).
// ============================================================================
module frame() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
opening = CUBE_SIZE - 2 * edge_d;
difference() {
union() {
// --- Coque arrondie évidée ---
difference() {
rounded_cube(CUBE_SIZE, CORNER_RADIUS);
cube([opening, opening, opening], center=true);
// Ouvertures pour les 6 faces
cube([opening, opening, CUBE_SIZE + 2], center=true); // ±Z
cube([CUBE_SIZE + 2, opening, opening], center=true); // ±X
cube([opening, CUBE_SIZE + 2, opening], center=true); // ±Y
}
// --- Corniches plates (assise pour PCB Carriers) ---
_frame_ledges();
// --- Plots de fixation M3 (4 par face) ---
_frame_mount_posts();
}
// --- Trous traversants M3 ---
_frame_mount_holes();
// --- Canaux de câblage ---
_cable_channels();
}
}
// Corniches planes pour l'assise des PCB Carriers
module _frame_ledges() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
lw = FRAME_LEDGE_WIDTH;
lh = FRAME_LEDGE_HEIGHT;
opening = CUBE_SIZE - 2 * edge_d;
outer = opening + 2 * lw;
// +Z et -Z
for (z = [-1, 1]) {
translate([0, 0, z * (half - edge_d + (z > 0 ? 0 : -lh))])
difference() {
linear_extrude(lh)
offset(r=CORNER_RADIUS)
offset(delta=-CORNER_RADIUS)
square([outer, outer], center=true);
translate([0, 0, -0.5])
linear_extrude(lh + 1)
square([opening, opening], center=true);
}
}
// +Y et -Y
for (y_dir = [-1, 1]) {
translate([0, y_dir * (half - edge_d + (y_dir > 0 ? 0 : -lh)), 0])
rotate([90, 0, 0])
difference() {
linear_extrude(lh)
offset(r=CORNER_RADIUS)
offset(delta=-CORNER_RADIUS)
square([outer, outer], center=true);
translate([0, 0, -0.5])
linear_extrude(lh + 1)
square([opening, opening], center=true);
}
}
// +X et -X
for (x_dir = [-1, 1]) {
translate([x_dir * (half - edge_d + (x_dir > 0 ? 0 : -lh)), 0, 0])
rotate([0, 90, 0])
difference() {
linear_extrude(lh)
offset(r=CORNER_RADIUS)
offset(delta=-CORNER_RADIUS)
square([outer, outer], center=true);
translate([0, 0, -0.5])
linear_extrude(lh + 1)
square([opening, opening], center=true);
}
}
}
// Plots de fixation (poteau cylindrique + base renforcée)
module _frame_mount_posts() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
post_h = 8;
post_d = 7;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
// Face +Z et -Z
for (z = [-1, 1]) {
for (xy = mount_positions) {
translate([xy[0], xy[1], z * (half - edge_d + (z > 0 ? 0 : -post_h))])
cylinder(d=post_d, h=post_h);
}
}
// Face +Y et -Y
for (y_dir = [-1, 1]) {
for (xz = mount_positions) {
translate([xz[0], y_dir * (half - edge_d + (y_dir > 0 ? 0 : -post_h)), xz[1]])
rotate([90, 0, 0])
cylinder(d=post_d, h=post_h);
}
}
// Face +X et -X
for (x_dir = [-1, 1]) {
for (yz = mount_positions) {
translate([x_dir * (half - edge_d + (x_dir > 0 ? 0 : -post_h)), yz[0], yz[1]])
rotate([0, 90, 0])
cylinder(d=post_d, h=post_h);
}
}
}
// Trous traversants M3 dans les plots
module _frame_mount_holes() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
for (z = [-1, 1]) {
for (xy = mount_positions) {
translate([xy[0], xy[1], z * half])
cylinder(d=FRAME_MOUNT_DIAM, h=20, center=true);
}
}
for (y_dir = [-1, 1]) {
for (xz = mount_positions) {
translate([xz[0], y_dir * half, xz[1]])
rotate([90, 0, 0])
cylinder(d=FRAME_MOUNT_DIAM, h=20, center=true);
}
}
for (x_dir = [-1, 1]) {
for (yz = mount_positions) {
translate([x_dir * half, yz[0], yz[1]])
rotate([0, 90, 0])
cylinder(d=FRAME_MOUNT_DIAM, h=20, center=true);
}
}
}
// Canaux pour câbles JST
module _cable_channels() {
ch_w = 10;
ch_h = 6;
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
for (z = [-1, 1]) {
translate([0, 0, z * (half - edge_d/2)])
cube([ch_w, ch_h, edge_d + 2], center=true);
}
for (y = [-1, 1]) {
translate([0, y * (half - edge_d/2), 0])
cube([ch_w, edge_d + 2, ch_h], center=true);
}
for (x = [-1, 1]) {
translate([x * (half - edge_d/2), 0, 0])
cube([edge_d + 2, ch_w, ch_h], center=true);
}
}
// ============================================================================
// PIÈCE 2 : BATTERY CRADLE (Support Batterie)
// ============================================================================
// Berceau pour batterie LiPo, se visse au frame via 4 vis M2.5.
// ============================================================================
module battery_cradle() {
bw = BATT_WIDTH + 2 * BATT_TOLERANCE;
bl = BATT_LENGTH + 2 * BATT_TOLERANCE;
bh = BATT_HEIGHT + BATT_TOLERANCE;
wall = 2;
difference() {
// Berceau avec parois
union() {
cube([bl + 2*wall, bw + 2*wall, bh + wall], center=true);
// Pieds de fixation (4 coins)
for (x = [-1, 1], y = [-1, 1]) {
translate([x * (bl/2), y * (bw/2), -(bh + wall)/2])
cylinder(d=6, h=2);
}
}
// Évidement
translate([0, 0, wall])
cube([bl, bw, bh + wall], center=true);
// Ouverture pour les fils
translate([0, bw/2 + wall, 0])
cube([20, wall*3, bh - wall], center=true);
// Trous de fixation M2.5
for (x = [-1, 1], y = [-1, 1]) {
translate([x * (bl/2), y * (bw/2), 0])
cylinder(d=2.7, h=20, center=true);
}
}
}
// ============================================================================
// PIÈCE 3 : PCB CENTRAL MOUNT (Support PCB Central)
// ============================================================================
// Plateforme avec 4 entretoises pour le PCB central (ESP32-S3).
// ============================================================================
module pcb_central_mount() {
pcb_l = CENTRAL_PCB_LENGTH;
pcb_w = CENTRAL_PCB_WIDTH;
standoff_h = 5;
standoff_d = 6;
hole_d = 2.5;
base_h = 2;
difference() {
union() {
// Base
cube([pcb_l + 10, pcb_w + 10, base_h], center=true);
// 4 entretoises
for (x = [-1, 1], y = [-1, 1]) {
translate([x * (pcb_l/2 - 5), y * (pcb_w/2 - 5), base_h/2])
cylinder(d=standoff_d, h=standoff_h);
}
}
// Trous dans les entretoises (M2.5)
for (x = [-1, 1], y = [-1, 1]) {
translate([x * (pcb_l/2 - 5), y * (pcb_w/2 - 5), 0])
cylinder(d=hole_d, h=20, center=true);
}
// Trous de fixation au frame (4 vis M2.5)
for (x = [-1, 1], y = [-1, 1]) {
translate([x * (pcb_l/2 + 3), y * (pcb_w/2 + 3), 0])
cylinder(d=2.7, h=20, center=true);
}
}
}
// ============================================================================
// PIÈCE 4-9 : PCB CARRIERS (Supports PCB pour chaque face)
// ============================================================================
// Support qui porte le PCB Anneau + PCB Bouchon, se visse sur les 4 plots du frame.
// ============================================================================
module pcb_carrier_standard() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
carrier_depth = 10; // Profondeur du support
opening = CUBE_SIZE - 2 * edge_d;
difference() {
union() {
// Plaque de base
linear_extrude(carrier_depth)
offset(r=CORNER_RADIUS - 2)
offset(delta=-(CORNER_RADIUS - 2))
square([opening - 4, opening - 4], center=true);
// Rebord pour PCB Anneau
translate([0, 0, carrier_depth - LED_RING_HEIGHT - 0.5])
_ring_holder();
// Support PCB Bouchon (central)
translate([0, 0, carrier_depth - LED_RING_HEIGHT - CAP_CONNECTOR_H - CAP_PCB_HEIGHT - 0.5])
cylinder(d=CAP_PCB_DIAM + 2, h=CAP_CONNECTOR_H + CAP_PCB_HEIGHT + 0.5);
// Oreilles de fixation (4 coins)
_carrier_mount_ears();
}
// Ouverture centrale (air + câblage)
translate([0, 0, -0.5])
cylinder(d=AIR_HOLE_DIAM, h=carrier_depth + 1);
// Évidement pour le PCB Anneau
translate([0, 0, carrier_depth - LED_RING_HEIGHT])
difference() {
cylinder(d=LED_RING_DIAM_EXT - 2, h=LED_RING_HEIGHT + 1);
cylinder(d=LED_RING_DIAM_INT + 2, h=LED_RING_HEIGHT + 2, center=true);
}
// Évidement pour le PCB Bouchon
translate([0, 0, carrier_depth - LED_RING_HEIGHT - CAP_CONNECTOR_H - CAP_PCB_HEIGHT])
cylinder(d=CAP_PCB_DIAM, h=CAP_CONNECTOR_H + CAP_PCB_HEIGHT + 1);
// Trous de fixation M3
_carrier_mount_holes();
}
}
// Rebord de maintien pour le PCB Anneau
module _ring_holder() {
ext = LED_RING_DIAM_EXT + 1;
int_ = LED_RING_DIAM_INT - 1;
h = 2;
difference() {
cylinder(d=ext, h=h);
translate([0, 0, -0.5])
cylinder(d=int_, h=h + 1);
// Encoches pour les connecteurs
for (a = [0, 90, 180, 270]) {
rotate([0, 0, a])
translate([ext/2-1, 0, 0])
cube([4, 6, h + 1], center=true);
}
}
}
// Oreilles de fixation pour les PCB Carriers
module _carrier_mount_ears() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
ear_size = 12;
ear_h = 10;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
for (xy = mount_positions) {
translate([xy[0], xy[1], ear_h/2])
cube([ear_size, ear_size, ear_h], center=true);
}
}
// Trous de fixation dans les oreilles
module _carrier_mount_holes() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
for (xy = mount_positions) {
translate([xy[0], xy[1], -1])
cylinder(d=FRAME_MOUNT_DIAM, h=20);
}
}
// Variante NFC (dessus)
module pcb_carrier_nfc() {
difference() {
union() {
pcb_carrier_standard();
// Ajout d'un support renforcé pour le PN532
translate([0, 0, 10 - NFC_PCB_HEIGHT - 0.5])
_nfc_holder();
}
// Évidement pour le module NFC
translate([0, 0, 10 - NFC_PCB_HEIGHT - 0.5])
cube([NFC_PCB_SIZE, NFC_PCB_SIZE, NFC_PCB_HEIGHT + 1], center=true);
}
}
module _nfc_holder() {
wall = 1.5;
size = NFC_PCB_SIZE + 2 * wall;
h = NFC_PCB_HEIGHT + 2;
difference() {
cube([size, size, h], center=true);
translate([0, 0, wall])
cube([NFC_PCB_SIZE, NFC_PCB_SIZE, h], center=true);
}
}
// Variante Qi (dessous)
module pcb_carrier_qi() {
difference() {
union() {
pcb_carrier_standard();
// Support pour bobine Qi
translate([0, 0, 2])
_qi_holder();
}
// Évidement pour la bobine Qi
translate([0, 0, 2])
cylinder(d=QI_COIL_DIAM, h=QI_COIL_HEIGHT + 1);
}
}
module _qi_holder() {
wall = 1.5;
h = QI_COIL_HEIGHT + 1;
difference() {
cylinder(d=QI_COIL_DIAM + 2*wall, h=h);
translate([0, 0, -0.5])
cylinder(d=QI_COIL_DIAM, h=h + 1);
}
// Support PCB récepteur
translate([0, QI_COIL_DIAM/2 + 5, 0])
difference() {
cube([QI_PCB_LENGTH + 2*wall, QI_PCB_WIDTH + 2*wall, QI_PCB_HEIGHT + 1], center=true);
cube([QI_PCB_LENGTH, QI_PCB_WIDTH, QI_PCB_HEIGHT + 2], center=true);
}
}
// ============================================================================
// PIÈCE 10-15 : FACE COVERS (Coques extérieures)
// ============================================================================
// Coque visible avec membrane tactile centrale + gorge joint O-ring.
// Se visse sur le PCB Carrier (4 vis M3).
// ============================================================================
module face_cover_standard() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
face_size = CUBE_SIZE - 2 * PRINT_TOLERANCE;
cover_depth = 6; // Profondeur de la coque (hors membrane)
difference() {
union() {
// Coque extérieure arrondie
difference() {
linear_extrude(cover_depth)
offset(r=CORNER_RADIUS)
offset(delta=-CORNER_RADIUS)
square([face_size - 2*edge_d + 6, face_size - 2*edge_d + 6], center=true);
translate([0, 0, WALL_THICKNESS])
linear_extrude(cover_depth)
offset(r=max(0.5, CORNER_RADIUS - WALL_THICKNESS))
offset(delta=-max(0.5, CORNER_RADIUS - WALL_THICKNESS))
square([face_size - 2*edge_d + 6 - 2*WALL_THICKNESS,
face_size - 2*edge_d + 6 - 2*WALL_THICKNESS], center=true);
}
// Membrane tactile centrale
cylinder(d=FACE_CENTER_DIAM + 10, h=FACE_MEMBRANE_T);
// Oreilles de fixation
_cover_mount_ears(cover_depth);
}
// Zone tactile amincie
translate([0, 0, FACE_MEMBRANE_T])
cylinder(d=FACE_CENTER_DIAM, h=cover_depth);
// Gorge joint O-ring (intérieure)
translate([0, 0, cover_depth - FACE_SEAL_GROOVE_D])
_seal_groove(face_size - 2*edge_d + 2);
// Trous de fixation M3
_cover_mount_holes();
}
}
module _cover_mount_ears(depth) {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
ear_size = 12;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
for (xy = mount_positions) {
translate([xy[0], xy[1], depth/2])
cube([ear_size, ear_size, depth], center=true);
}
}
module _cover_mount_holes() {
half = CUBE_SIZE / 2;
edge_d = FRAME_EDGE_DEPTH;
mount_positions = [
[ (half - edge_d - 10), (half - edge_d - 10)],
[-(half - edge_d - 10), (half - edge_d - 10)],
[ (half - edge_d - 10), -(half - edge_d - 10)],
[-(half - edge_d - 10), -(half - edge_d - 10)]
];
for (xy = mount_positions) {
translate([xy[0], xy[1], -1])
cylinder(d=FRAME_MOUNT_DIAM, h=20);
// Lamage tête de vis
translate([xy[0], xy[1], -1])
cylinder(d=FRAME_MOUNT_HEAD, h=FRAME_MOUNT_DEPTH + 1);
}
}
// Gorge joint O-ring
module _seal_groove(size) {
w = FACE_SEAL_GROOVE_W;
d = FACE_SEAL_GROOVE_D;
inset = 3;
linear_extrude(d + 0.1)
difference() {
offset(r=CORNER_RADIUS - inset)
offset(delta=-(CORNER_RADIUS - inset))
square([size - 2*inset, size - 2*inset], center=true);
offset(r=max(0.5, CORNER_RADIUS - inset - w))
offset(delta=-max(0.5, CORNER_RADIUS - inset - w))
square([size - 2*inset - 2*w, size - 2*inset - 2*w], center=true);
}
}
// Variantes NFC et Qi (identiques à standard pour l'extérieur)
module face_cover_nfc() {
difference() {
face_cover_standard();
// Amincissement supplémentaire pour l'antenne NFC (optionnel)
translate([0, 0, -0.5])
cylinder(d=NFC_ANTENNA_DIAM, h=FACE_MEMBRANE_T + 0.5);
}
}
module face_cover_qi() {
difference() {
face_cover_standard();
// Amincissement supplémentaire pour la bobine Qi
translate([0, 0, -0.5])
cylinder(d=QI_COIL_DIAM + 4, h=WALL_THICKNESS);
}
}

View file

@ -1,362 +0,0 @@
# Spécifications — Companion Cube Interactif
> **Version :** 0.2.0
> **Date :** 2026-02-08
> **Statut :** Brouillon — en cours de validation
---
## Sommaire
- [Partie 1 — Spécifications Fonctionnelles](#partie-1--spécifications-fonctionnelles)
- [Partie 2 — Spécifications Techniques (v1.0 — ESP32-S3)](#partie-2--spécifications-techniques-v10--esp32-s3)
- [Partie 3 — Passerelle (Gateway/Dock)](#partie-3--passerelle-gatewaydock)
- [Partie 4 — Feuille de Route](#partie-4--feuille-de-route)
---
# Partie 1 — Spécifications Fonctionnelles
## 1. Vision Produit
Un cube interactif de ~12 cm inspiré du **Weighted Companion Cube** (Aperture Science), repositionné dans un contexte **opérationnel** (IT/DevOps, Défense, Coordination d'équipes).
Le cube est un **terminal physique configurable** : chaque face est un bouton lumineux dont le rôle, les animations et les actions sont définis depuis le serveur **ops-devices**. Il communique localement (ESP-NOW / BLE) avec une **passerelle** (dock) qui assure la connectivité réseau (WiFi / 4G) vers le serveur MQTT.
### Contraintes de Design
* **Esthétique :** Coque grise mate, coins renforcés, centre de chaque face marqué d'un symbole configurable servant de bouton tactile. Le reste de la face est lumineux (LEDs RGB diffuses).
* **Diffusion Lumineuse :** Aucun "point chaud" visible. La lumière doit être diffuse, organique, et sembler émaner de la face.
* **Étanchéité Visuelle :** Aucun connecteur ni bouton visible de l'extérieur (charge par induction Qi, interaction par tactile et NFC).
* **Taille :** ~12 cm d'arête.
* **Environnement :** Bureau / QG intérieur (pas de contrainte IP spécifique pour la v1).
---
## 2. Architecture Globale (2 Tiers)
```
┌─────────────┐ ESP-NOW / BLE ┌──────────────────┐ WiFi / 4G ┌─────────────┐
│ CUBE(s) │ ◄─────────────────────► │ PASSERELLE │ ◄───────────────► │ SERVEUR │
│ ESP32-S3 │ Communication locale │ (Dock/Gateway) │ MQTT │ ops-devices │
│ Simple │ │ ESP32-S3 + WiFi │ │ Mosquitto │
└─────────────┘ │ Charge Qi TX │ └─────────────┘
└──────────────────┘
```
* **Cube** : Terminal autonome, pas de WiFi activé. Communication locale uniquement.
* **Passerelle** : Dock physique qui charge le cube (Qi) et fait le pont entre la communication locale et le réseau (WiFi/4G → MQTT).
* **Serveur** : ops-devices existant avec Mosquitto (MQTT), backend API, et interface web de configuration.
---
## 3. Disposition des Faces
| Face | Position | Rôle Matériel | Rôle Logiciel |
|------|----------|---------------|---------------|
| **1** | Dessus | NFC (PN532) + Tactile + LEDs | Configurable (NFC + action) |
| **2** | Face avant | Tactile + LEDs | Configurable (action + statut) |
| **3** | Face droite | Tactile + LEDs | Configurable (action + statut) |
| **4** | Face arrière | Tactile + LEDs | Configurable (action + statut) |
| **5** | Face gauche | Tactile + LEDs | Configurable (action + statut) |
| **6** | Dessous | Récepteur Qi + LEDs (indicateur charge) | Statut batterie / charge |
> **Les rôles logiciels des faces 1 à 5 sont entièrement configurables depuis le serveur.** L'utilisateur définit pour chaque face : l'action déclenchée, et l'animation associée à chaque état.
---
## 4. Système d'Actions Configurable
Chaque face (1-5) est un **slot d'action** configurable avec les propriétés suivantes :
### 4.1 Définition d'une Action
| Propriété | Description | Exemple |
|-----------|-------------|---------|
| `label` | Nom affiché dans l'interface | "Redémarrer Nginx" |
| `icon` | Symbole sur la face physique (optionnel, gravé/imprimé) | 🔄 |
| `trigger` | Type d'interaction physique | `tap`, `long_press`, `double_tap` |
| `mqtt_topic` | Topic MQTT publié lors du déclenchement | `ops/cube/{id}/action/face2` |
| `mqtt_payload` | Payload JSON envoyé | `{"action": "restart", "target": "nginx"}` |
| `confirm` | Nécessite une confirmation (double tap) | `true` / `false` |
### 4.2 États Visuels d'une Action
Chaque action possède des **états visuels** animés sur les LEDs de la face :
| État | Description | Animation par défaut |
|------|-------------|---------------------|
| `idle` | En veille, aucune activité | Pulsation lente (couleur configurable) |
| `listening` | En écoute / surveillance active | Respiration douce (bleu) |
| `triggered` | Action déclenchée par l'utilisateur | Flash blanc → onde de couleur |
| `success` | Action exécutée avec succès | Vert fixe 3s → retour idle |
| `error` | Échec de l'action | Rouge clignotant 5s → retour idle |
| `alert` | Alerte reçue du serveur | Animation insistante (rouge pulsé) |
| `warning` | Avertissement reçu | Orange pulsé |
| `info` | Information reçue | Bleu flash unique |
### 4.3 Cas d'Usage Prioritaires
#### IT/DevOps — Monitoring & Actions Serveurs
* Face = un service (Nginx, Postgres, API, etc.)
* LED verte = service UP, rouge = DOWN
* Tap = voir le statut détaillé (clignotement code)
* Double Tap = redémarrer le service
* Alerte push = notification serveur DOWN
#### Défense — C2 (Command & Control)
* Face = un canal ou une zone d'opération
* LED = statut de la zone (vert = RAS, orange = vigilance, rouge = alerte)
* Tap = acquitter l'alerte
* Long Press = envoyer un signal prédéfini (point de ralliement, repli, etc.)
* NFC = identification opérateur / changement de profil
#### Coordination d'Équipes
* Face = une équipe ou un membre
* LED = disponibilité (vert = dispo, orange = occupé, rouge = indisponible)
* Tap = envoyer un "ping" à l'équipe
* Double Tap = demande de contact urgent
* Long Press = déclencher un timer partagé (briefing, pause)
---
## 5. Modes de Fonctionnement
### A. Mode Veille (Idle)
* Pulsation lente ("respiration") sur les 6 faces, couleur selon l'état de chaque action configurée.
* **Économie :** Luminosité réduite à 10% après 5 minutes sans interaction.
* **Deep Sleep :** Après 30 minutes sans interaction ni message MQTT, le cube entre en deep sleep. Réveil par toucher ou message ESP-NOW.
### B. Mode Interaction Tactile
* **Tap :** La face réagit par un flash blanc + exécute l'action `tap` configurée.
* **Long Press (> 2s) :** Exécute l'action `long_press` configurée.
* **Double Tap :** Exécute l'action `double_tap` configurée (avec confirmation si activée).
### C. Mode NFC (Face du dessus uniquement)
* **Tag d'identification :** Change le profil utilisateur du cube (jeu d'actions différent).
* **Tag d'action :** Déclenche une action spécifique associée au tag.
* **Smartphone :** Le cube peut envoyer une URL (NDEF) ou recevoir des données de configuration.
* **Cube-à-Cube :** Approcher deux cubes = transfert du profil de configuration (WiFi, serveur, actions).
### D. Mode Alerte (Push depuis le serveur)
* Le serveur envoie un message MQTT → la passerelle le relaie au cube en ESP-NOW/BLE.
* La face concernée passe en animation d'alerte.
* L'utilisateur acquitte en touchant la face → le cube envoie un ACK au serveur.
---
## 6. Gestion de l'Énergie et Alertes
* **Autonomie cible :** 12-24h en usage actif (LEDs à luminosité moyenne, communication ESP-NOW active).
* **Charge Qi (sur la passerelle) :** Quand posé sur le dock, les faces passent en orange pulsé → vert fixe à 100%.
* **Charge USB-C :** Accessible en ouvrant une face (maintenance uniquement).
* **Alerte Batterie Faible :** La face du dessous clignote en rouge sous 15%. Sous 5%, toutes les faces clignotent rouge.
* **Sécurité Thermique :** Si >45°C, le cube coupe les LEDs et émet un flash rouge toutes les 10s.
---
## 7. Configuration et Administration
### Via l'interface web ops-devices
* Associer un cube (pairing BLE via la passerelle).
* Configurer les actions de chaque face (drag & drop).
* Créer des profils d'actions (IT, C2, Coordination) interchangeables via NFC.
* Voir l'état temps réel de chaque cube (batterie, température, dernière interaction).
* Mettre à jour le firmware (OTA via la passerelle).
### Via la passerelle
* Appairage automatique des cubes à proximité.
* Relais transparent MQTT ↔ ESP-NOW.
* File d'attente des messages si le cube est en deep sleep.
* Logs locaux des interactions.
---
# Partie 2 — Spécifications Techniques (v1.0 — ESP32-S3)
## 1. Architecture Matérielle du Cube
* **MCU :** ESP32-S3 (WiFi désactivé pour économie d'énergie, BLE + ESP-NOW actifs).
* **Touch :** 6 pads capacitifs natifs (pins TOUCH de l'ESP32-S3).
* **NFC :** 1x PN532 (face du dessus) — Communication I2C.
* **LEDs :** 6 lignes Data indépendantes (1 par face), LEDs WS2812B.
* **Batterie :** LiPo > 2000mAh (volume disponible dans un cube de 12cm).
* **Charge :** Qi 5V (face du dessous) + USB-C (maintenance).
* **Jauge batterie :** CW2015 (I2C).
* **Thermique :** NTC 10kΩ sur batterie, reliée à l'ESP32 et au TP4056.
* **Charge controller :** TP4056 avec protection.
---
## 2. Design des Faces ("Sandwich" à 2 étages)
### Étage A : PCB "Bouchon" (Capteur)
* **Face du dessus :** PN532 (NFC) + pad capacitif central.
* **Faces 2-5 :** Pad capacitif central uniquement.
* **Fixation :** Plaque contre la membrane de 1.0mm, enfiché dans l'étage B via connecteurs 2.54mm.
### Étage B : PCB "Anneau" (LEDs & Structure)
* **LEDs :** 12x WS2812B disposées en cercle, orientées vers l'extérieur.
* **Connectivité :** Reçoit le PCB Bouchon (femelle 2.54) et renvoie les signaux au PCB central (JST).
* **Flux d'air :** Large ouverture centrale (~30mm) pour la dissipation thermique.
---
## 3. Gestion de l'Énergie & Thermique
### Alimentation & Charge
* **Batterie :** LiPo 3.7V — capacité cible 2000-3000mAh (le volume d'un cube de 12cm le permet).
* **Charge Qi :** Récepteur Qi 5V sur la face du dessous.
* **Charge USB-C :** Résistances 5.1kΩ, accès maintenance.
* **Contrôleur :** TP4056 avec protection (CHRG/STDBY).
### Monitoring (I2C & Analogique)
* **Jauge :** CW2015 — suivi précis de la capacité via I2C.
* **Thermique :** NTC 10kΩ sur batterie, reliée à l'ESP32-S3 (ADC) et au TP4056 (coupure auto >45°C).
### Estimation d'Autonomie
| Mode | Consommation estimée | Autonomie (2500mAh) |
|------|---------------------|---------------------|
| Deep Sleep | ~10µA | ~28 000h (3+ ans) |
| Veille (LEDs 10%) | ~50mA | ~50h |
| Actif (LEDs 50% + ESP-NOW) | ~150mA | ~16h |
| Actif max (LEDs 100% + BLE) | ~250mA | ~10h |
---
## 4. Mapping des Pins (ESP32-S3)
| Fonction | Pin GPIO | Détails |
| :--- | :--- | :--- |
| **LEDs Face 1 (dessus)** | GPIO 1 | Data WS2812B |
| **LEDs Face 2 (avant)** | GPIO 2 | Data WS2812B |
| **LEDs Face 3 (droite)** | GPIO 4 | Data WS2812B |
| **LEDs Face 4 (arrière)** | GPIO 5 | Data WS2812B |
| **LEDs Face 5 (gauche)** | GPIO 6 | Data WS2812B |
| **LEDs Face 6 (dessous)** | GPIO 7 | Data WS2812B (indicateur charge) |
| **I2C SDA** | GPIO 8 | Bus commun (PN532 + CW2015) |
| **I2C SCL** | GPIO 9 | Bus commun |
| **Touch Face 1** | GPIO 10 | Capacitif natif ESP32-S3 |
| **Touch Face 2** | GPIO 11 | Capacitif natif |
| **Touch Face 3** | GPIO 12 | Capacitif natif |
| **Touch Face 4** | GPIO 13 | Capacitif natif |
| **Touch Face 5** | GPIO 14 | Capacitif natif |
| **Touch Face 6** | GPIO 21 | Capacitif natif (optionnel) |
| **TP4056 CHRG** | GPIO 15 | État de charge |
| **TP4056 STDBY** | GPIO 16 | Charge terminée |
| **NTC (Temp)** | GPIO 17 (ADC) | Surveillance thermique |
| **USB D-** | GPIO 19 | Programmation & Debug natif |
| **USB D+** | GPIO 20 | Programmation & Debug natif |
---
## 5. Structure Mécanique (OpenSCAD)
* **Dimensions :** Cube de ~12 cm d'arête.
* **Chambre de Diffusion :** "Ring Shroud" de 6mm entre les LEDs et la paroi extérieure.
* **Membranes Tactiles :** Épaisseur réduite à 1.0mm au centre de chaque face.
* **Système de Clips :** Ergots type baïonnette pour verrouiller le PCB Anneau.
* **Circulation d'air :** Effet cheminée par les centres évidés des faces.
* **Matériau :** Impression 3D (PLA/PETG) pour la petite série (5-10 unités).
* **Finition :** Peinture grise mate + vernis de protection.
---
## 6. Stack Logicielle (PlatformIO / Arduino Framework)
| Composant | Librairie / Technologie | Rôle |
|-----------|------------------------|------|
| **Animations LEDs** | FastLED | Gestion des 6 sorties WS2812B |
| **NFC** | Adafruit PN532 | Lecture/écriture tags NFC (I2C) |
| **Communication locale** | ESP-NOW (natif) | Liaison cube ↔ passerelle |
| **BLE** | NimBLE-Arduino | Configuration initiale, pairing |
| **OTA** | Via passerelle (ESP-NOW) | Mise à jour firmware sans fil |
| **Jauge batterie** | CW2015 driver | Monitoring SOC (I2C) |
| **Sécurité thermique** | Custom | Priorité aux alertes NTC + SOC |
| **Persistance config** | Preferences (NVS) | Stockage local des actions/profils |
### Structure MQTT (Topics)
```
ops/cube/{cube_id}/status → Statut général (batterie, temp, uptime)
ops/cube/{cube_id}/face/{n}/state → État visuel de la face n
ops/cube/{cube_id}/face/{n}/action → Action déclenchée par l'utilisateur
ops/cube/{cube_id}/config → Configuration des actions (serveur → cube)
ops/cube/{cube_id}/config/ack → Acquittement de configuration
ops/cube/{cube_id}/alert → Alertes push (serveur → cube)
ops/cube/{cube_id}/alert/ack → Acquittement d'alerte
ops/cube/{cube_id}/ota → Commande de mise à jour OTA
ops/cube/{cube_id}/ota/status → Statut de la mise à jour
```
---
# Partie 3 — Passerelle (Gateway/Dock)
## 1. Rôle
La passerelle est le pont entre les cubes (communication locale) et le serveur (réseau IP). Elle sert également de station de charge Qi.
## 2. Matériel
* **MCU :** ESP32-S3 (WiFi activé) ou ESP32-C6 (WiFi + Thread).
* **Communication locale :** ESP-NOW (cube ↔ passerelle).
* **Communication réseau :** WiFi → MQTT vers le serveur ops-devices.
* **Charge :** Émetteur Qi 5V intégré dans le dock.
* **Connectivité optionnelle :** Module 4G (SIM7600) pour les déploiements sans WiFi.
* **Alimentation :** USB-C 5V (branchée en permanence).
## 3. Fonctionnalités Logicielles
* **Relais MQTT ↔ ESP-NOW :** Traduction transparente des messages.
* **File d'attente :** Stockage des messages si un cube est en deep sleep, livraison au réveil.
* **Multi-cube :** Gestion de 1 à N cubes simultanément.
* **OTA Proxy :** Télécharge le firmware depuis le serveur, le transmet au cube en ESP-NOW.
* **Watchdog :** Détection de perte de connexion (cube ou serveur) avec alertes.
* **Logs :** Historique local des interactions (SPIFFS/LittleFS).
---
# Partie 4 — Feuille de Route
## v1.0 — MVP (ESP32-S3)
* Cube fonctionnel avec 6 faces LED + tactile + 1 NFC.
* Passerelle ESP-NOW → WiFi → MQTT.
* Configuration des actions via l'interface web ops-devices.
* Charge Qi + USB-C.
* Firmware OTA via passerelle.
* **Cible :** 5-10 unités, budget 50-100€/cube (hors passerelle).
## v1.1 — Améliorations
* Synthèse vocale (petit haut-parleur, annonce des actions/alertes).
* Synchronisation multi-cubes (actions coordonnées via ESP-NOW).
* Profils NFC (changement de jeu d'actions par tag).
## v2.0 — Version Souveraine (STM32WB)
* Remplacement ESP32-S3 par STM32WB (STMicroelectronics, français).
* Communication Thread/Matter native.
* Touch via contrôleur externe (AT42QT1070).
* OTA via BLE (STM32 OTA DFU).
* Certification et durcissement pour usage Défense.
* Aucune technologie non-européenne dans la chaîne.
---
## BOM Estimatif (v1.0 — par cube)
| Composant | Référence | Qté | Prix unitaire | Total |
|-----------|-----------|-----|---------------|-------|
| ESP32-S3-WROOM-1 | Espressif | 1 | ~4€ | 4€ |
| WS2812B (LEDs) | — | 72 (12/face) | ~0.05€ | 4€ |
| PN532 (NFC) | NXP | 1 | ~3€ | 3€ |
| TP4056 (Charge) | — | 1 | ~0.50€ | 1€ |
| CW2015 (Jauge) | CellWise | 1 | ~1€ | 1€ |
| NTC 10kΩ | — | 1 | ~0.10€ | 0€ |
| Batterie LiPo 2500mAh | — | 1 | ~8€ | 8€ |
| Récepteur Qi | — | 1 | ~3€ | 3€ |
| PCBs (6 anneaux + 6 bouchons + central) | JLCPCB | 13 | ~1€ | 13€ |
| Connecteurs JST + 2.54mm | — | lot | ~3€ | 3€ |
| Boîtier impression 3D | PLA/PETG | 1 | ~5€ | 5€ |
| USB-C + passifs | — | lot | ~2€ | 2€ |
| **TOTAL** | | | | **~47€** |
> Budget restant (~3-53€) disponible pour la finition, les imprévus, et l'outillage.