4 décembre 2018
Transformez une roue IKEA LUSTIGT en une roue connectée WiFi 🎡
8 minutes de lecture
Chez Premier Octet, bien que nous soyons spécialisés dans le développement d'applications React et React Native, rien ne nous empêche de nous glisser sporadiquement dans le trépident domaine de l'IoT. Il y a quelques semaines, je me suis rendu chez IKEA pour y acheter des panneaux SKÅDIS et manger des hot-dogs 🌭. Comme l'impose la conception architecturale d'un IKEA, je suis passé par tous les endroits possibles et imaginables du magasin et notamment celui des enfants, pour tomber nez à nez avec LUSTIGT, une "roue de la chance" en bois. Ni une, ni deux, je me suis dit qu'il fallait ✨ la connecter à Internet ✨.
Quelques hot-dogs plus tard, je repars donc, avec LUSTIGT (et SKÅDIS) dans les bras avec la ferme intention de hacker ce beau jouet.
TL;DR
Trouver le hack
Les choses sérieuses commencent : dans l'idéal je souhaite pouvoir déclencher un webhook contenant la valeur de la case une fois la roue tournée. J'envisage donc d'utiliser 24 émetteurs pour chaque case ainsi qu'un capteur maître pour les lire. Ils doivent être sans fil en raison de la mobilité de la roue. Le capteur maître doit lire les valeurs et les envoyer via une connexion WiFi. Autre contrainte, les émetteurs sur la roue doivent être assez fins : l'espace étant inférieur à 2cm…
Cela semble le job parfait pour du RFID !
La RFID
La RFID utilise des champs éléctro-magnétiques pour identifier des tags. Ceux-ci contiennent une puce électronique leur permettant de recevoir et de répondre aux requêtes radio émises depuis l’émetteur. Les tags existent sous de nombreuses formes : cartes, porte-clés, badges mais aussi sous forme de stickers. Ces-derniers sont idéaux pour ce projet, car ils sont :
- Très fins (< 1mm) ;
- Passifs (auto-alimentés par le champ radio) ;
- Facilement positionnables ;
- Peu onéreux (environ 20€ les 50 stickers).
L'idée est donc de coller 24 stickers derrière chaque case de la roue et d'ajouter un lecteur RFID à sa base. Et pour envoyer les données à Internet ? Un Photon !
Le Photon
Il y a 4 ans, j'ai découvert le Photon : je n'ai depuis plus utilisé d'Arduino. Comme ce-dernier, le Photon est un micro-contrôleur avec beaucoup d'avantages :
- WiFi embarqué ;
- Facilement programmable (comme l'Arduino) ;
- Même PINs que l'Arduino ;
- Accessible via une API ;
- Web based IDE ;
- Très bonne documentation ;
- Open-source ;
- Petit et accessible ($19).
Le Photon va nous permettre de contrôler le système RFID et d'envoyer les données à Internet.
Matériel nécessaire
Voici la liste du matériel nécessaire, comptez environ 60€ pour le tout :
- 1 x IKEA Lustigt Roue de la chance (20€);
- 1 x Particle Photon avec headers (20€) ;
- 1 x RC522 carte (5€);
- 24 x RFID sticker tag (18€ les 50);.
- 5 x câbles femelle vers femelle ;
Et comme autres outils (que vous devriez avoir sous la main) :
-
Fer à souder ;
-
Du double face ;
-
Une alimentation 5V micro USB (type Raspberry).
Le montage du hardware
Le montage est facile : il s'agit uniquement de souder les headers sur votre carte RFID afin de la connecter facilement au Photon. Pour cela insérez le header (celui coudé) dans les slots de la carte RC522, et faites-y 6 points de soudure :
Brancher les cartes
Une fois le header soudé, vous pouvez relier la carte RFID à votre Photon, pour cela utilisez les câbles femelles en suivant ce schéma :
Function RC522 Pin Photon Pin
-------------------------------------
Reset RST D2
SPI SS SDA D1
SPI MOSI MOSI A5
SPI MISO MISO A4
SPI SCK SCK A3
IRQ IRQ -
GROUND GND GND
POWER 3.3V 3.3V # 🚨 Not 5V!
Attention, la carte RFID ne supporte pas le 5v, veillez à bien la brancher sur la pin 3.3V du Photon. Je me suis limité à ce montage qui est très simple, pour les plus assidus vous pouvez créer votre propre PCB en passant par exemple par Eagles.
Configurer votre Photon
Si vous n'avez pas encore configuré votre Photon, suivez les instructions sur https://setup.particle.io :
Une fois votre Photon connecté à Internet et configuré, il est temps de faire un peu de collage…
Coller les stickers RFID
La stack hardware est prête, il nous faut maintenant coller nos stickers RFID derrière chaque case de la roue. Veillez à bien les placer au centre des deux bâtons à chaque fois pour éviter des collisions d'UID lors de la lecture des tags.
Fixer la carte RFID
Il ne reste plus qu'à fixer la carte RC522 sur la base de la roue, pour cela j'ai simplement utilisé du double face et fixé le Photon derrière :
Code côté hardware
Il est maintenant temps de coder ! Nous allons envoyer notre programme à notre Photon afin de piloter notre stack RFID. Si vous n'êtes pas familier avec l'écosystème de Particle, sachez qu'il dispose d'un IDE en ligne permettant d'envoyer vos sketchs (au format .ino) directement sur votre Photon via le WiFi.
Lecture des tags UID
Il existe déjà une librairie pour s'interfacer avec le module RFID, vous n'avez donc qu'à l'ajouter puis l'importer dans votre projet.
Voici le code minimal pour lire les UIDs de chaque tag :
#include "MFRC522/MFRC522.h"
#define SS_PIN D1
#define RST_PIN D2
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup() {
mfrc522.setSPIConfig();
mfrc522.PCD_Init();
}
void loop() {
if (!mfrc522.PICC_IsNewCardPresent() && !mfrc522.PICC_ReadCardSerial()) {
return
}
String UID = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
UID += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
UID += String(mfrc522.uid.uidByte[i], HEX);
}
mfrc522.PICC_HaltA();
Particle.publish("WHEEL_VALUE", UID, 5, PRIVATE);
}
Le cloud Particle nous permet de partager facilement des données via la méthode Particle.publish()
. Une fois publié vous pouvez voir l'évenement WHEEL_VALUE
dans la partie event de la console :
Vous pouvez alors tourner votre roue afin de lire un par un les UIDs de vos tags et les classer par ordre croissant dans un tableau :
String wheelCases[] = {
"044653120a3c80", // case 1
"043653120a3c80",
"04513d92ec5a80",
"043c4892ec5a81",
"04374892ec5a81",
"04454792ec5a81",
"043f4e92ec5a81",
"04244e92ec5a81",
"04414892ec5a81",
"04294e92ec5a81",
"042e4e92ec5a81",
"04354e92ec5a81", // case 12
"043a4e92ec5a81",
"04444e92ec5a81",
"046f4e92ec5a81",
"044c4c92ec5a81",
"045f4392ec5a80",
"04484d92ec5a81",
"04e64992ec5a80",
"04514c92ec5a81",
"04564c92ec5a81",
"04f04992ec5a80",
"043e53120a3c80",
"042d53120a3c80" // case 24
};
Après cette tâche un peu fastidieuse, nous allons améliorer notre code afin qu'il n'envoie un événement uniquement lorsque la roue s'arrête de tourner (une sorte de debouncing).
Implémenter la logique
Voici mon algo pour envoyer uniquement la dernière valeur : lorsque aucune valeur n'a été lue depuis 1s, nous publions l'évenement avec Particle.publish(). Je publie également un évenement SPINNING
afin de rajouter un feedback côté UI :
#include "MFRC522/MFRC522.h"
#define SS_PIN D1
#define RST_PIN D2
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
String wheelCases[] = {
// Ajouter votre mapping d'UIDs (voir ci-dessus)
};
long chrono = 0;
long debounceDelay = 1000;
bool isInit = true;
bool isSpinning = false;
String valueToCommit = "";
void setup() {
mfrc522.setSPIConfig();
mfrc522.PCD_Init();
}
void loop() {
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
if (!isSpinning) {
Particle.publish("SPINNING", "true", 5, PRIVATE);
isSpinning = true;
}
String UID = "";
String label = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
UID += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
UID += String(mfrc522.uid.uidByte[i], HEX);
}
mfrc522.PICC_HaltA();
for (int i=0; i<24; i++){
if (wheelCases[i] == UID){
label = i + 1;
}
}
if (!isInit) {
valueToCommit = label;
chrono = millis();
}
isInit = false;
}
if (valueToCommit != "" && (millis() - chrono) > debounceDelay) {
Particle.publish("WHEEL_VALUE", valueToCommit, 5, PRIVATE);
Particle.publish("SPINNING", "false", 5, PRIVATE);
valueToCommit="";
isSpinning = false;
}
}
Applications
Notre roue est prête ! Elle envoie désormais un évenement SPINNING
dès que la roue tourne et la valeur de la dernière case via l'évenement WHEEL_VALUE
. Il est maintenant temps d'utiliser ces évenements !
Hook everything
Grâce à la platforme Particle Web, nous pouvons facilement lier des évenements à des webhooks grâce à la partie intégration de la Particle Console :
Nous pouvons ainsi poster la valeur tirée au sort sur Slack en appelant un incoming webhook lors d'un évenement WHEEL_VALUE
.
Autre bonne nouvelle : IFTTT offre une intégration de la platforme Particle permettant de connecter n'importe quel évenement à l'ensemble des services IFTTT : Slack, Google Doc, Twitter… Comme par exemple, insérer la valeur de chaque tirage dans un Google spreadsheet, ou bien la tweeter :
Application React
Chez Premier Octet, le React c'est notre dada… Nous n'allions pas passer à côté d'une occasion de développer une application affichant le résultat de notre tirage en temps réel !
Particle met à disposition une librairie JS permettant d'utiliser le Particle Cloud : particle-api-js. Nous allons donc pouvoir souscrire à nos évenements SPINNING
et WHEEL_VALUE
avec la méthode getEventStream() :
import React, { Component } from 'react'
import Particle from 'particle-api-js'
const DEVICE_ID = 'XXXXX'
const TOKEN = 'XXXXX'
const particle = new Particle()
class App extends Component {
state = { isSpinning: false, event: null }
componentDidMount() {
particle.getEventStream({ deviceId: DEVICE_ID, auth: TOKEN }).then((stream) => {
stream.on('event', (event) => {
if (event.name === 'SPINNING') {
this.setState({ isSpinning: true })
} else if (event.name === 'WHEEL_VALUE') {
this.setState({ event: event.data, isSpinning: false })
}
})
})
}
render() {
return (
<div className="app">
<div className="caption">
{this.state.isSpinning ? (
<span>✨ Spinning ✨️</span>
) : (
<>
{this.state.event ? (
<span>🕺 {this.state.event} 🕺</span>
) : (
<span>Spin the wheel! 🤸♀️</span>
)}
</>
)}
</div>
</div>
)
}
}
Nous affichons maintenant la dernière valeur sur laquelle s'est arretée la roue. C'est bien mais l'idée initiale est de pouvoir tirer au sort une valeur parmi un groupe de choix : restaurants, noms de personnes…
Nous allons donc créer un spreadsheet sur Google Doc afin de pouvoir mapper nos cases à nos labels. Il suffit de créer un classeur pour chaque jeu de données (foods, users) :
… et de les lier grâce à l'API de Google Spreadsheet (nécessite de créer une clé d'API) :
const API_KEY = 'xx'
const SPREADSHEET_ID = 'xx'
const SHEET_NAME = 'food'
loadLabels = async () => {
const data = await fetch(
`https://sheets.googleapis.com/v4/spreadsheets/${SPREADSHEET_ID}/values/${SHEET_NAME}!a1:a26?key=${API_KEY}`
).then((res) => res.json())
return data.values
}
Vous pouvez ensuite ajouter dans votre UI, un select pour choisir le jeu de données voulu avant de lancer la roue 🎡. Retrouvez le code complet sur le repository GitHub.
Hack hack hack
Connectez la roue IKEA LUSTIGT aux Internets a été drôlement intéressant, j'ai été surpris que tout ce beau monde s'imbrique correctement et marche du premier coup… Si vous en avez encore sous le pied, vous pouvez aller encore plus loin :
- ajouter un feedback sur la roue lorsqu'elle tourne (LED, musique…) ;
- faire une intégration plus propre du Photon et de la board RFID sur la roue (👋imprimante 3D) ;
- trouver d'autres applications !
Si vous montez votre propre roue, envoyez-nous vos photos et applications !
Happy hacking ! 🎈