1
0
Fork 0

Utils: Introduce utils and animation helpers

The next commit implements the LCD and animation support. To enable an easy
way to create animations for it, add details in the README regarding the way
the SD card is used and how the files need to be formatted.

Create the Utils directory and add a Rust project to do the bit flipping and
a script that automates all the steps described in the README.
Add a README for Utils explaining how to use them.
This commit is contained in:
Teo-CD 2023-10-21 21:55:54 +01:00
parent 6c5a670366
commit e21123538b
8 changed files with 274 additions and 0 deletions

View file

@ -21,6 +21,9 @@ de la carte électronique :
- Jouer des sons - Jouer des sons
- Contrôle du gain - Contrôle du gain
Les données utilisées pour l'affichage sur le LCD ou pour la sortie audio doivent
être chargés depuis une carte SD insérée dans la carte microcontrôleur.
En plus de contrôler la carte, le firmware doit pouvoir intéragir avec une En plus de contrôler la carte, le firmware doit pouvoir intéragir avec une
application développée sur Unity. application développée sur Unity.
En premier lieu, il doit renvoyer des identifiants correspondants aux tags RFID En premier lieu, il doit renvoyer des identifiants correspondants aux tags RFID
@ -69,6 +72,100 @@ mise à jour et suivi des dépendances.
La structure du code assume une carte Teensy. En particulier, il est conçu pour la La structure du code assume une carte Teensy. En particulier, il est conçu pour la
[Teensy 4.1](https://www.pjrc.com/store/teensy41.html). [Teensy 4.1](https://www.pjrc.com/store/teensy41.html).
# Carte SD et données
La carte SD est utilisée pour lire les animations et les sons, lors de l'utilisation
des figurines ou pour des cas génériques (démarrage, batterie faible…).
Le firmware attend les fichiers dans certains endroits et sous certaines formes,
détaillés ici.
## Structure de fichiers
Les fichiers sur la carte SD sont structurés comme suit :
```
/
├── RANDONNEE/
│ ├── ANIM/
│ │ ├── 00
│ │ ├── 01
│ │ ├── 02
│ │ ├── ..
│ │ └── XX
│ └── audio.wav
├── RESTO/
│ ├── ANIM/
│ │ └── ..
│ └── audio.wav
├── .../
└── SYS/
├── ANIM*/
├── ../
└── *.wav
```
Tous les fichiers liés à une figurine en particulier doivent être placés dans un
dossier associé au nom de la figurine.
Les noms attendus sont ceux des tableaux définis dans `src/IDs.cpp`.
Les différentes images d'une même animation doivent être dans un même dossier,
numérotés dans l'ordre avec deux chiffres et sans extension, les fichiers audio
doivent garder leur extension.
Le dossier SYS sera utilisé pour toute interaction qui n'est pas liée à une figurine.
## Structure des données
### Audio
Le format audio supporté est assez précis : les fichiers doivent être des WAV
16 bit PCM, 44100 Hz.
Mono ou stéréo n'est pas important : la carte ne supporte qu'un haut-parleur,
les deux canaux stéréo seront moyennés `(Gauche+Droit)/2`.
### Animations
Le format des animations est d'autant plus précis et particulier et demandera
probablement du traitement pour qu'elles soient correctes.
Tout d'abord, chaque image de l'animation doit être dans son propre fichier, un
seul fichier pour l'animation (comme un gif) n'est pas possible.
L'écran LCD choisi est un module pour écran Nokia 5110 de 84x48 pixels monochromes,
les images de l'animation doivent donc être à ce format et monochromes.
L'image attendue est exclusivement une bitmap d'un bit par pixel, sans aucune
autre information.
De plus, il est probable que les bits attendus soient inversés par rapport à ceux
produits "logiquement" depuis un programme normal. Par exemple `0b11001100` devrait
en réalité être `0b00110011`.
Je n'ai pas connaissance d'un format connu qui peut être généré depuis un logiciel
et répondre à ces conditions, cependant il est possible d'y arriver automatiquement
avec quelques opérations de transformation.
Pour la facilité, il est donc possible d'arriver aux fichiers individuels requis
à partir d'un simple gif aux proportions correctes.
À l'aide d'[ImageMagick](https://imagemagick.org/), on peut découper le gif en
ses images individuelles, et les convertir en un format de bitmap presque correct
(aux bits inversés) avec la commande suivante :
```shell
convert -resize 84x48 -coalesce <animation>.gif -threshold 50% %02d.mono
```
Il peut être nécessaire d'inverser les bits comme mentionné plus haut et je n'ai
pas trouvé de façon existante de le faire.
Vous trouverez donc dans `Utils/bitmap_helper` un projet Rust qui permet de
réaliser cette opération sur les fichiers produits par la commande ci-dessus.
Une fois compilé, vous pouvez convertir les fichiers `.mono` dans le format adapté
(avec une extension `.bin` en les passant à l'utilitaire :
```shell
bitmap_helper *.mono
```
N'oubliez pas de retirer l'extension et de les placer dans un dossier `ANIM` sur
la carte SD, comme décrit ci-dessus.
Un script bash est aussi fourni pour automatiser tout ce processus :
`Utils/convertAnimGif.sh`.
# Interfaçage avec l'application Unity # Interfaçage avec l'application Unity
Afin de permettre la détection automatique de l'objet, la communication se fait Afin de permettre la détection automatique de l'objet, la communication se fait

2
Utils/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.gif
ANIM*/

56
Utils/README.md Normal file
View file

@ -0,0 +1,56 @@
# Utilitaires
Ce sous-dossier rassemble divers utilitaires liés au projet.
# Conversion des animations
Deux outils sont disponnibles pour aider à la conversion : un pour faire l'inversion
de bit nécessaire, et un script pour automatiser tout le protocole.
Pour des détails sur le format de sortie, référez-vous au README principal.
## bitmap_helper
Un projet Rust a été créé pour permettre la conversion d'une bitmap MONO gérée
par ImageMagick en une bitmap correcte pour le LCD, en inversant les bits de chaque
octet.
Il est possible qu'une version compilée soit disponible dans les versions sur
Github.
### Compilation
Pour le compiler, il suffit d'utiliser Cargo, qui peut être installé via
[rustup](https://rustup.rs/) par exemple.
Une fois Rust et Cargo disponibles, la compilation peut être faite depuis le
dossier `bitmap_helper` avec la commande suivante :
```shell
cargo build
```
L'exécutable produit est alors `bitmap_helper/target/debug/bitmap_helper`.
### Utilisation
L'utilitaire peut convertir plusieurs fichiers `.mono` à la fois, passés directement
sur la ligne de commande.
```shell
bitmap_helper/target/debug/bitmap_helper <frame00>.mono <frame01>.mono ...
```
Tout autre format de fichier ne sera pas traité.
⚠️ La détection du format n'est basée que sur les extensions !
## convertAnimGif.sh
Ce script permet d'automatiser tout le protocole de conversion d'un ou plusieurs
gifs en suites de bitmaps utilisables par le projet.
Il prend en argument un ou plusieurs fichier(s) `.gif` et produit les bitmap
individuelles correctement numérotées dans un dossier portant le nom du fichier
original.
```shell
./convertAnimGif.sh <animation1>.gif <animation2>.gif ...
```
Il suffit ensuite de déposer les dossiers sur la carte SD, sous les dossiers correspondants
(voir noms dans `src/IDs.cpp`) et les renommer pour ne laisser que `ANIM`.

1
Utils/bitmap_helper/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Utils/bitmap_helper/Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitmap_helper"
version = "0.1.0"

View file

@ -0,0 +1,8 @@
[package]
name = "bitmap_helper"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,51 @@
use std::fs::File;
use std::fs::write;
use std::env;
use std::io::Read;
fn main() {
let args: Vec<String> = env::args().collect();
for arg in &args[1..] {
println!("Processing : {}", arg);
if !arg.ends_with(".mono") {
eprintln!("{} : unsupported file type.", arg);
continue;
}
let file_r = File::open(arg);
if file_r.is_err() {
eprintln!("{} : could not open file.", arg);
continue;
}
let mut image_data = vec![];
if file_r.unwrap().read_to_end(&mut image_data).is_err() {
eprintln!("{} : could not read file.", arg);
continue;
}
let out_file = arg.replace(".mono", ".bin");
flip_bits(&mut image_data);
if write(&out_file, &image_data).is_err() {
eprintln!("Failed to write bit flipped bitmap to file : {}", out_file);
}
println!("Processing successful, flipped bits and wrote to {}", out_file);
}
}
/// Flips the bits of each individual byte (except zeroes).
/// This would transform 0b11001100 in 0b00110011.
fn flip_bits(data: &mut Vec<u8>) {
for source_id in 0..data.len() {
let mut new_int = 0;
for i in 0..8 {
if data[source_id] == 0 { break; }
new_int |= ((data[source_id] & (1 << 7 - i)) >> 7 - i) << i;
}
data[source_id] = new_int;
}
// for i in 0..data.len() {
// print!("{:2X}", data[i]);
// if i > 0 && i%16 == 0 { println!(); }
// }
// println!();
}

52
Utils/convertAnimGif.sh Executable file
View file

@ -0,0 +1,52 @@
#!/bin/bash
set -euo pipefail
if [[ $# -eq 0 ]]; then
echo "This script expects the name of the files to convert as arguments"
exit 1
fi
if [[ ! $(command -v convert) ]]; then
echo "Could not find 'convert' utility, cannot proceed."
exit 1
fi
# Get the absolute path to the source of the script
scriptSrcDir="$(dirname -- "$0")"
pushd "$scriptSrcDir"
scriptSrcDir="$(pwd)"
popd
if [[ $(command -v bitmap_helper) ]]; then
bmp_help="$(command -v bitmap_helper)"
elif [[ $(command -v "$scriptSrcDir"/bitmap_helper/target/debug/bitmap_helper) ]]; then
bmp_help="$scriptSrcDir"/bitmap_helper/target/debug/bitmap_helper
elif [[ $(command -v "$scriptSrcDir"/bitmap_helper) ]]; then
bmp_help="$scriptSrcDir"/bitmap_helper
else
echo "Could not find 'bitmap_helper'."
echo "Have you compiled it or placed it in the same directory as the script ?"
exit 1
fi
for gifToConvert in "$@"; do
if [[ -z "$(echo "$gifToConvert" | grep -e 'gif')" ]]; then
echo "Cannot convert $gifToConvert : not a gif."
continue
fi
animDir=ANIM_"${gifToConvert//.gif//}"
mkdir "$animDir"
pushd "$animDir"
convert -resize 84x48 -coalesce ../"$gifToConvert" -threshold 50% %02d.mono
"$bmp_help" ./*.mono
# Clean up and remove the extension
rm -f ./*.mono
for f in ./*.bin; do
mv "$f" "${f//.bin/}"
done
popd
done