feat: updated blog to be in english
Signed-off-by: Louis Vallat <contact@louis-vallat.fr>
Before Width: | Height: | Size: 11 KiB |
@ -1,260 +0,0 @@
|
|||||||
+++
|
|
||||||
title = "Git Workflow"
|
|
||||||
description = "Git est un outil de versionnage très utile, voici comment je m'en sers."
|
|
||||||
date = 2021-05-10
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["git", "workflow"]
|
|
||||||
+++
|
|
||||||
|
|
||||||
Git est un outil de versionnage très utile, voici comment je m'en sers.
|
|
||||||
|
|
||||||
<!-- more -->
|
|
||||||
|
|
||||||
> À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant,
|
|
||||||
> il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez
|
|
||||||
> exhaustif dans mes explications).
|
|
||||||
|
|
||||||
[TL;DR](#tldr)
|
|
||||||
|
|
||||||
## Les fondations
|
|
||||||
|
|
||||||
Vous êtes bien installé devant votre écran, les doigts ne demandent qu'à
|
|
||||||
furieusement taper les touches de votre clavier, mais avant le top départ, il faut
|
|
||||||
préparer le terrain ! Voici quelques commandes **primordiales** (pour ne pas dire
|
|
||||||
**obligatoires**) à réaliser avant de se lancer dans un projet.
|
|
||||||
|
|
||||||
Premièrement, je n'aime pas la manière qu'a git de faire des commits de fusion
|
|
||||||
lors d'un pull qui se passe mal (divergence entre votre version locale et distante
|
|
||||||
par exemple, aka. vous avez committé et quelqu'un d'autre aussi en même temps).
|
|
||||||
Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire
|
|
||||||
(fast-forward), et sinon de faire automatiquement un rebase :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git config pull.rebase true
|
|
||||||
```
|
|
||||||
|
|
||||||
En cas de divergence, un simple `git rebase origin/branche_concernée` vous sortira
|
|
||||||
d'affaire !
|
|
||||||
|
|
||||||
Ensuite, il nous arrive de vouloir avoir une vue plus graphique de notre historique
|
|
||||||
git. Voici un alias, à placer dans votre `~/.gitconfig`, qui vous permet d'afficher
|
|
||||||
un graphique de vos branches et vos commits de manière plus visuelle que le `git log`
|
|
||||||
de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
[alias]
|
|
||||||
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
|
|
||||||
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
|
|
||||||
lg = !"git lg1"
|
|
||||||
```
|
|
||||||
|
|
||||||
Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir
|
|
||||||
d'alias, cette commande fait quelque chose d'assez similaire :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git log --all --decorate --oneline --graph
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour avoir des messages de commits unifiés et cohérents avec de jolis alias :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
fix = "!f() { git commit -sm \"fix: $1\"; }; f"
|
|
||||||
feat = "!f() { git commit -sm \"feat: $1\"; }; f"
|
|
||||||
misc = "!f() { git commit -sm \"misc: $1\"; }; f"
|
|
||||||
wip = "!f() { git commit -sm \"wip: $1\"; }; f"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Des branches, des branches, et encore des branches
|
|
||||||
|
|
||||||
Quand on travaille à plusieurs sur git, il devient presque obligatoire d'avoir
|
|
||||||
des branches pour éviter de se sentir "à l'étroit" dans son `repo`. Pour pouvoir
|
|
||||||
collaborer et avancer sur un projet en groupe sans se marcher sur les pieds les
|
|
||||||
uns des autres, les branches deviennent l'outil indispensable de git.
|
|
||||||
|
|
||||||
J'utilise les branches de la manière suivante :
|
|
||||||
|
|
||||||
- les branches ont une durée de vie **très courte**
|
|
||||||
- une fois que la branche a rempli son rôle et qu'elle a été fusionnée, elle doit être supprimée
|
|
||||||
- une branche par fonctionnalité ou par résolution de problème
|
|
||||||
- les noms des branches sont libres *tant qu'ils sont clairs*
|
|
||||||
- c'est mieux d'avoir comme nom la fonctionnalité développée ou son rôle
|
|
||||||
- lorsqu'une branche sert à développer une résolution de bug ou un `fix`, il est mieux de l'inclure dans le nom
|
|
||||||
- lorsque vous travaillez à plusieurs sur la même fonctionnalité, il est conseillé de
|
|
||||||
faire une branche par personne en partant de la branche princpale de cette fonctionnalité
|
|
||||||
|
|
||||||
Évidemment, une branche fait exception : `master` (ou `main`, peu importe),
|
|
||||||
qui est une version "propre", "stable", et qui doit présenter le projet dans un état
|
|
||||||
fonctionnel, tel qu'il serait montré à un client ou à un responsable.
|
|
||||||
|
|
||||||
Il est possible d'avoir une seconde branche sur les projets plus importants, qui se
|
|
||||||
nomme alors `develop` et qui est la version en développement actif d'un projet,
|
|
||||||
mais qui ne peut pas être fusionnée sur la branche princpale pour des raisons de
|
|
||||||
stabilité par exemple, ou qui n'est pas prête pour la mise en production.
|
|
||||||
|
|
||||||
Pour créer une branche fille à partir de la branche sur laquelle vous êtes :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout -b nom_de_la_nouvelle_branche
|
|
||||||
```
|
|
||||||
|
|
||||||
Et pour basculer de branche en branche :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout nom_de_la_branche
|
|
||||||
```
|
|
||||||
|
|
||||||
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git branch
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rebaser, c'est la clef
|
|
||||||
|
|
||||||
> Cette partie peut être ignorée si vous ne pouvez pas réaliser de `push force`
|
|
||||||
> (ou équivalent). En effet, un `rebase` modifie fondamentalement l'historique de
|
|
||||||
> votre git. Si vous n'êtes pas sûr de vous, n'en faites pas. Je ne suis pas
|
|
||||||
> responsable si vous perdez des données.
|
|
||||||
|
|
||||||
Votre fonctionnalité est développée et testée, vous voulez la fusionner avec votre
|
|
||||||
branche parente ? Vous voulez récupérer ce qui est sur votre branche parente et
|
|
||||||
que vous aimeriez récupérer sur la votre ? Il est temps de rebase !
|
|
||||||
|
|
||||||
Lorsque l'on crée une branche, celle-ci a un `commit` de "départ", depuis lequel
|
|
||||||
elle se *base* pour faire du versionnage en parallèle des autres branches. Parfois
|
|
||||||
il est nécessaire de récupérer du travail qui a été mis sur la branche mère dans
|
|
||||||
notre branche de travail, par exemple la résolution d'un bogue handicapant.
|
|
||||||
|
|
||||||
Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre.
|
|
||||||
Prenons le schéma suivant :
|
|
||||||
|
|
||||||
![Schéma d'un rebase](images/git_rebase.svg)
|
|
||||||
|
|
||||||
Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons
|
|
||||||
que vous êtes en train de développer une fonctionnalité importante, et qu'un bug
|
|
||||||
vous ralentit dans l'exécution de vos tests, mais qu'il a été réglé après la création
|
|
||||||
de votre branche, et qu'il est disponible sur `master`. Pour le récupérer sur votre
|
|
||||||
branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`.
|
|
||||||
La commande pour faire ceci serait alors :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git rebase nom_de_branche_parente
|
|
||||||
```
|
|
||||||
|
|
||||||
Il est important de noter que vous risquez d'avoir des conflits au cours de cette
|
|
||||||
opération. En cas de conflit, vous pouvez les régler, puis faire `git add fichier_en_conflit`
|
|
||||||
et enfin `git rebase --continue`. Cela aura pour effet de **réécrire** l'historique
|
|
||||||
git de votre branche, d'où l'importance de faire un push en force, puisque votre
|
|
||||||
version locale et la/les version(s) distante(s) n'auront plus rien à voir.
|
|
||||||
|
|
||||||
### Quel rapport avec ce git flow
|
|
||||||
|
|
||||||
Comme dit plus haut, les branches ont une existence précise et bien définie dans
|
|
||||||
le temps. Une branche n'a pas a être fusionnée dans sa branche parente tant que
|
|
||||||
son contenu n'est pas complet, ou au moins utilisable. Cela ne fait pas vraiment
|
|
||||||
de sens de "polluer" une branche parente dans le seul but d'en récupérer le nouveau
|
|
||||||
contenu. Les opérations de rebase sont donc ici fréquentes.
|
|
||||||
|
|
||||||
## Fusion non préparée = fusion refusée
|
|
||||||
|
|
||||||
Votre fonctionnalité ou correctif est terminé(e), votre branche est prête à être
|
|
||||||
fusionnée ? En vérité, pas tout à fait. Il faut maintenant préparer la fusion !
|
|
||||||
|
|
||||||
Afin de faciliter au maximum la fusion de votre branche, il va falloir faire un
|
|
||||||
dernier rebase. Le but de ce rebase est ici de vérifier que le rôle de la branche
|
|
||||||
et du contenu qu'elle propose sont toujours fonctionnels et qu'aucun bogue n'a été
|
|
||||||
intruduit par les nouveautés disponibles sur la branche parente. Cela va aussi
|
|
||||||
vous permettre de gérer les conflits avec la branche mère au sein même de la votre.
|
|
||||||
Une fusion n'introduira donc pas de bogue, et vous restez pleinement responsable
|
|
||||||
de votre branche.
|
|
||||||
|
|
||||||
Enfin, vous n'êtes pas responsables de la fusion, un collègue ou responsable va
|
|
||||||
d'abord vérifier votre code et le contenu que vous apportez et approuver (ou rejeter)
|
|
||||||
la fusion que vous demandez. Grâce aux opérations de rebasage successifs que vous
|
|
||||||
avez fait durant la durée de vie de votre branche, la fusion se fera sans encombre.
|
|
||||||
|
|
||||||
À noter que si vous avez des outils tels que les [Merge Requests](https://docs.gitlab.com/ee/user/project/merge_requests/getting_started.html)
|
|
||||||
chez GitLab, ou [Pull Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
|
|
||||||
chez GitHub, n'hésitez surtout pas à les utiliser ! Ces outils permettront de garder
|
|
||||||
un suivi des fusions, et d'expliquer son but à un responsable qui ne les a pas
|
|
||||||
forcément toutes en tête.
|
|
||||||
|
|
||||||
Pour fusionner une branche dans celle sur laquelle vous vous trouvez :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Au revoir vieille branche
|
|
||||||
|
|
||||||
> Les opérations décrites ici peuvent entraîner une perte de code. Je ne suis pas
|
|
||||||
> responsable si vous supprimez une branche avant qu'elle n'ait été fusionnée.
|
|
||||||
> Ne supprimez une branche que si vous êtes sûr qu'elle n'a plus d'utilité.
|
|
||||||
|
|
||||||
Votre branche est désormais fusionnée ? C'est la fin pour elle, il est désormais
|
|
||||||
temps de lui dire au revoir !
|
|
||||||
|
|
||||||
Commençons d'abord par la supprimer sur le repo distant :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git push -d origin nom_de_branche
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, le serveur distant s'appelle `origin`, comme c'est le cas la plupart du temps.
|
|
||||||
|
|
||||||
Vos collègues peuvent synchroniser leurs branches et faire disparaître leurs
|
|
||||||
références locales à des branches fantômes distantes avec la commande `git fetch -p` (le
|
|
||||||
`-p` étant ici pour "prune").
|
|
||||||
|
|
||||||
Enfin, il est temps de supprimer votre copie locale de cette branche :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git branch -d nom_de_branche
|
|
||||||
```
|
|
||||||
|
|
||||||
À noter que le `-d` ne supprimera votre branche que si elle a été fusionnée. Il est
|
|
||||||
possible de forcer la suppression d'une branche locale non fusionnée avec l'option
|
|
||||||
`-D` qui équivaut à un `git --delete --force` sur la branche.
|
|
||||||
|
|
||||||
"Don't cry because it's over, smile because it happened." - *Dr. Seuss*
|
|
||||||
|
|
||||||
## TL;DR {#tldr}
|
|
||||||
|
|
||||||
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
|
|
||||||
la version distante de notre branche, et sinon, de rebase notre branche automatiquement :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git config pull.rebase true
|
|
||||||
```
|
|
||||||
|
|
||||||
Ensuite on utilise une branche par fonctionnalité et par correctif. Une branche a
|
|
||||||
un rôle précis et une durée de vie très courte. Lorsque plusieurs développeurs
|
|
||||||
travaillent sur la même branche, ils ne doivent pas exclure la possibilité de créer
|
|
||||||
des branches personnelles.
|
|
||||||
|
|
||||||
`master` est en tous temps propre et présentable. Une branche intermédiaire peut
|
|
||||||
être utilisée pour éviter de polluer inutilement la branche principale. Elle pourra
|
|
||||||
s'appeler `develop` par exemple.
|
|
||||||
|
|
||||||
Les rebase sont fréquents et servent à récupérer sur une branche fille du nouveau
|
|
||||||
contenu de sa branche mère.
|
|
||||||
|
|
||||||
La fusion se déroule sans accroc, un rebase doit être fait avant pour gérer les
|
|
||||||
conflits sur la branches fille.
|
|
||||||
|
|
||||||
Une fois la branche fusionnée, elle doit être supprimée.
|
|
||||||
|
|
||||||
## Informations supplémentaires
|
|
||||||
|
|
||||||
### Vocabulaire
|
|
||||||
|
|
||||||
- `repo` : raccourci de `repository`, qui est l'environnement de travail de git,
|
|
||||||
c'est là dedans que l'on créé ses branches, et que l'on versionne son travail.
|
|
||||||
|
|
||||||
### Pour aller plus loin
|
|
||||||
|
|
||||||
- Le fonctionnement des objets, commits et arbres sur git (en profondeur) : [vidéo](https://youtu.be/MyvyqdQ3OjI).
|
|
||||||
- Le fonctionnement des branches sur git (en profondeur) : [vidéo](https://youtu.be/mhZQRBp8dXE).
|
|
||||||
- Comment git stocke son arborescence de fichiers : [article](https://book.git-scm.com/book/en/v2/Git-Internals-Git-Objects).
|
|
||||||
- Les commandes associées à `git branch` : [documentation](https://git-scm.com/docs/git-branch).
|
|
@ -1,286 +0,0 @@
|
|||||||
+++
|
|
||||||
title = "Robot timide – le robot qui fuit la lumière"
|
|
||||||
description = "Création d'un robot timide pour un projet scolaire."
|
|
||||||
date = 2022-05-19
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["maker", "fablab", "robotique"]
|
|
||||||
+++
|
|
||||||
|
|
||||||
Création d'un robot timide pour un projet scolaire.
|
|
||||||
|
|
||||||
> Article cloné depuis [Eirlab](https://www.eirlab.net/2022/05/19/robot-timide-le-robot-qui-fuit-la-lumiere/)
|
|
||||||
|
|
||||||
Dans le cadre de l’option Makers proposée en deuxième année à l’ENSEIRB-MATMECA,
|
|
||||||
en filière Informatique, un robot timide a vu le jour au sein d’Eirlab.
|
|
||||||
Cet article est un guide pour expliquer comment reproduire notre travail, dans
|
|
||||||
le plus pur esprit Maker.
|
|
||||||
|
|
||||||
<!-- more -->
|
|
||||||
|
|
||||||
![Une vue de 3/4 face du robot finalisé](images/robot_timide_header.jpg)
|
|
||||||
|
|
||||||
## Concept de base
|
|
||||||
|
|
||||||
Nous voulions mettre en oeuvre un robot qui détecterait en temps réel la
|
|
||||||
luminosité autour de lui et ce dans le but de se déplacer vers l’endroit le
|
|
||||||
plus sombre de la pièce dans lequel il se trouve, et de s’y cacher. Un autre
|
|
||||||
mode est aussi disponible, permettant non pas d’aller vers l’endroit le plus
|
|
||||||
sombre, mais à l’inverse de fuir l’endroit le plus lumineux, pour avoir deux
|
|
||||||
manières de voir le problème.
|
|
||||||
|
|
||||||
![Une vue de face du robot timide dans sa forme finale.](images/robot_timide_enseirb.jpg)
|
|
||||||
|
|
||||||
## Du matériel
|
|
||||||
|
|
||||||
Avant de pouvoir travailler sur une partie logicielle du projet, il faut
|
|
||||||
évidemment avoir une base physique pour accueillir et exécuter ce code.
|
|
||||||
|
|
||||||
À noter que ce projet n’a nécessité aucune dépense et a été réalisé entièrement
|
|
||||||
avec du matériel déjà disponible au sein du Fablab.
|
|
||||||
|
|
||||||
### Liste du matériel
|
|
||||||
|
|
||||||
Pour pouvoir reproduire ce projet, il vous faudra avoir en votre possession les
|
|
||||||
éléments suivants :
|
|
||||||
|
|
||||||
- 1x Arduino Uno (ou clone équivalent) – c’est la tête pensante de ce projet
|
|
||||||
- 1x Arduino Motor Shield – pour contrôler les deux moteurs du robot
|
|
||||||
- 2x moteurs à courant continu, ici des FT DC 130D – pour se mouvoir dans l’espace
|
|
||||||
- 3x capteurs de distance à ultrasons HC-SR04 – pour éviter de rencontrer des murs trop souvent
|
|
||||||
- 4x photo résistances – une par côté, pour pouvoir détecter la luminosité autour de lui
|
|
||||||
- 4x résistances de 2kΩ – pour le circuit des photo résistances, permettant d’avoir une amplitude optimale
|
|
||||||
- 1x pile 9 Volts – pour alimenter tout le système
|
|
||||||
- 1x boîtier pour pile 9 Volts [OPTIONNEL] – pour éviter de perdre l’unique source d’alimentation du robot
|
|
||||||
|
|
||||||
### Base du châssis
|
|
||||||
|
|
||||||
Le châssis est une plaque en bois, ici du contreplaqué de 5 millimètres
|
|
||||||
d’épaisseur, qui a été usinée à l’aide de la découpeuse laser du Fablab. Elle
|
|
||||||
mesure 17×15,5 centimètres, et dispose de deux bords biseautés pour avoir une
|
|
||||||
vue frontale des obstacles que le robot pourrait rencontrer. Ici, ces biseaux
|
|
||||||
ont des angles de 20 degrés, sur 4,5 centimètres. La plaque de base est ensuite
|
|
||||||
percée pour accueillir les différents modules du robot. Les éléments les plus
|
|
||||||
légers ou risquant moins de se détacher sont fixés par un simple système de
|
|
||||||
mâchoire, prenant en étau la plaque de bois dans une forme de U.
|
|
||||||
|
|
||||||
![La forme de U pour prendre la plaque de base en mâchoire et tenir un capteur par pression.](images/u_shape.jpg)
|
|
||||||
|
|
||||||
### Alimentation
|
|
||||||
|
|
||||||
Pour l’alimentation du robot, on vient envoyer 9 Volts d’une source quelconque,
|
|
||||||
d’une pile ou d’une alimentation stabilisée par exemple, dans l’entrée `VIN`
|
|
||||||
de l’Arduino, qui sera après régulée en interne pour donner les différents
|
|
||||||
rails d’alimentation. Cette entrée d’alimentation est connectée en interne au
|
|
||||||
port DC “barrel jack” de l’Arduino, donc avant les régulateurs de courant,
|
|
||||||
et prend donc des tensions entre 5 et 9 Volts.
|
|
||||||
|
|
||||||
Dans ce projet, nous utilisons une pile 9 Volts rechargeable dans un boîtier
|
|
||||||
vissé au châssis.
|
|
||||||
|
|
||||||
![Une vue de la pile 9 Volts que nous utilisons, rechargeable en micro USB.](images/pile9v.jpg)
|
|
||||||
|
|
||||||
### Moteurs
|
|
||||||
|
|
||||||
Les moteurs ici sont vissés au châssis, car juste pris en mâchoire ils ne
|
|
||||||
tiennent pas et se détachent à la moindre accélération.
|
|
||||||
|
|
||||||
Ils sont directement alimentés et pilotés via la carte Arduino Motor Shield,
|
|
||||||
qui permet de sélectionner leur vitesse sur une échelle de `0` à `255`,
|
|
||||||
d’actionner les freins et de leur donner un sens de rotation, et ce très
|
|
||||||
simplement dans le code.
|
|
||||||
|
|
||||||
Des roues ont été imprimées en 3D pour aller sur les embouts des moteurs, et un
|
|
||||||
joint torique sert de pneu pour améliorer l’adhérence du robot et éviter les
|
|
||||||
démarrage en “burn”.
|
|
||||||
|
|
||||||
### PCB
|
|
||||||
|
|
||||||
Au début de ce projet, nous avons utilisé une carte de prototypage électronique,
|
|
||||||
aussi appelé breadboard. Dans un second temps et pour éviter toute déconnexion
|
|
||||||
de câbles liée à une potentielle accélération violente, nous avons fait un PCB,
|
|
||||||
soudé manuellement sur une carte de prototypage prévu à cet effet.
|
|
||||||
|
|
||||||
![Une vue du dessus de notre PCB fait main.](images/pcb_irl.jpg)
|
|
||||||
|
|
||||||
Au niveau électronique, le PCB est routé de la manière suivante :
|
|
||||||
|
|
||||||
![Le routage du PCB sur une carte de protypage de type Breadboard.](images/pcb_fritzing.png)
|
|
||||||
|
|
||||||
Les différents composants viennent se connecter sur le PCB au moyen de
|
|
||||||
différentes broches femelles, pour que ce dernier puisse être détaché et modifié
|
|
||||||
sans devoir dessouder le moindre composant.
|
|
||||||
|
|
||||||
## Et du logiciel
|
|
||||||
|
|
||||||
D’un point de vue logiciel, notre projet est assez simple. En pseudo-algorithme,
|
|
||||||
on peut tout simplement le résumer à “on regarde parmi les 4 photo résistances
|
|
||||||
et on tourne ou avance vers celle ayant la valeur la plus sombre” dans le cas
|
|
||||||
où `followDark` est à `true`. Si au contraire cette valeur est à `false`, alors
|
|
||||||
il ira dans la direction opposée à la photorésistance ayant la plus haute valeur.
|
|
||||||
|
|
||||||
Une mesure des distances en face des trois capteurs à ultrasons permet d’arrêter
|
|
||||||
le robot (presque) avant collision frontale, et le robot ne se déplacera pas
|
|
||||||
tant que l’objet en face est toujours présent, et donc en théorie tant qu’il a
|
|
||||||
trouvé l’endroit le plus sombre.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define echo1Pin 5
|
|
||||||
#define trig1Pin 4
|
|
||||||
#define echo2Pin 7
|
|
||||||
#define trig2Pin 6
|
|
||||||
#define echo3Pin 10
|
|
||||||
#define trig3Pin 2
|
|
||||||
#define rotMot1Pin 12
|
|
||||||
#define brakeMot1Pin 9
|
|
||||||
#define vitMot1Pin 3
|
|
||||||
#define rotMot2Pin 13
|
|
||||||
#define brakeMot2Pin 8
|
|
||||||
#define vitMot2Pin 11
|
|
||||||
#define photoRes1Pin A2
|
|
||||||
#define photoRes2Pin A3
|
|
||||||
#define photoRes3Pin A4
|
|
||||||
#define photoRes4Pin A5
|
|
||||||
|
|
||||||
#define followDark false
|
|
||||||
|
|
||||||
long duration;
|
|
||||||
int distance;
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
pinMode(rotMot1Pin, OUTPUT);
|
|
||||||
pinMode(brakeMot1Pin, OUTPUT);
|
|
||||||
pinMode(vitMot1Pin, OUTPUT);
|
|
||||||
|
|
||||||
pinMode(rotMot2Pin, OUTPUT);
|
|
||||||
pinMode(brakeMot2Pin, OUTPUT);
|
|
||||||
pinMode(vitMot2Pin, OUTPUT);
|
|
||||||
|
|
||||||
pinMode(photoRes1Pin, INPUT);
|
|
||||||
pinMode(photoRes2Pin, INPUT);
|
|
||||||
pinMode(photoRes3Pin, INPUT);
|
|
||||||
pinMode(photoRes4Pin, INPUT);
|
|
||||||
|
|
||||||
pinMode(trig1Pin, OUTPUT);
|
|
||||||
pinMode(echo1Pin, INPUT);
|
|
||||||
pinMode(trig2Pin, OUTPUT);
|
|
||||||
pinMode(echo2Pin, INPUT);
|
|
||||||
pinMode(trig3Pin, OUTPUT);
|
|
||||||
pinMode(echo3Pin, INPUT);
|
|
||||||
|
|
||||||
delay(3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop(){
|
|
||||||
byte echoPins[3] = {echo1Pin,echo2Pin,echo3Pin};
|
|
||||||
byte trigPins[3] = {trig1Pin,trig2Pin,trig3Pin};
|
|
||||||
long int durations[3] = {};
|
|
||||||
long int distances[3] = {};
|
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++){
|
|
||||||
digitalWrite(trigPins[i], LOW);
|
|
||||||
delayMicroseconds(2);
|
|
||||||
|
|
||||||
digitalWrite(trigPins[i], HIGH);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
digitalWrite(trigPins[i], LOW);
|
|
||||||
durations[i] = pulseIn(echoPins[i], HIGH);
|
|
||||||
distances[i] = durations[i] * 0.034 / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int photoRes[4] = {};
|
|
||||||
Serial.print("Capteur infra : 1[");
|
|
||||||
photoRes[0] = analogRead(photoRes1Pin);
|
|
||||||
Serial.print(photoRes[0]);
|
|
||||||
Serial.print("] 2[");
|
|
||||||
photoRes[1] = analogRead(photoRes2Pin);
|
|
||||||
Serial.print(photoRes[1]);
|
|
||||||
Serial.print("] 3[");
|
|
||||||
photoRes[2] = analogRead(photoRes3Pin);
|
|
||||||
Serial.print(photoRes[2]);
|
|
||||||
Serial.print("] 4[");
|
|
||||||
photoRes[3] = analogRead(photoRes4Pin);
|
|
||||||
Serial.print(photoRes[3]);
|
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++){
|
|
||||||
Serial.print("] - distance(cm) : ");
|
|
||||||
Serial.print(distances[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
bool b = false;
|
|
||||||
for(int i = 0; i < 3; i++){
|
|
||||||
if (distances[i] < 30) {
|
|
||||||
b= true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int photoResRef = photoRes[0];
|
|
||||||
bool photoStop = true;
|
|
||||||
int photoResMinPos = 0;
|
|
||||||
|
|
||||||
if (followDark){
|
|
||||||
int photoResMin = 1024;
|
|
||||||
for (int i = 0; i < 4; i++){
|
|
||||||
if (photoResMin > photoRes[i]){
|
|
||||||
photoResMin = photoRes[i];
|
|
||||||
photoResMinPos = i;
|
|
||||||
}
|
|
||||||
if (abs(photoResRef - photoRes[i]) > 20){
|
|
||||||
photoStop = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
int photoResMax = 0;
|
|
||||||
for (int i = 0; i < 4; i++){
|
|
||||||
if (photoResMax < photoRes[i]){
|
|
||||||
photoResMax = photoRes[i];
|
|
||||||
photoResMinPos = (i + 2)%4;
|
|
||||||
}
|
|
||||||
if (abs(photoResRef - photoRes[i]) > 20){
|
|
||||||
photoStop = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.println(photoResMinPos);
|
|
||||||
|
|
||||||
if (b || photoStop){
|
|
||||||
digitalWrite(brakeMot1Pin, HIGH);
|
|
||||||
digitalWrite(brakeMot2Pin, HIGH);
|
|
||||||
} else {
|
|
||||||
digitalWrite(brakeMot1Pin, LOW);
|
|
||||||
digitalWrite(brakeMot2Pin, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (photoResMinPos == 0){
|
|
||||||
digitalWrite(rotMot1Pin, LOW);
|
|
||||||
digitalWrite(rotMot2Pin, LOW);
|
|
||||||
}
|
|
||||||
if (photoResMinPos == 1){
|
|
||||||
digitalWrite(rotMot1Pin, LOW);
|
|
||||||
digitalWrite(rotMot2Pin, LOW);
|
|
||||||
}
|
|
||||||
if (photoResMinPos == 2){
|
|
||||||
digitalWrite(rotMot1Pin, HIGH);
|
|
||||||
digitalWrite(rotMot2Pin, HIGH);
|
|
||||||
}
|
|
||||||
if (photoResMinPos == 3){
|
|
||||||
digitalWrite(rotMot1Pin, HIGH);
|
|
||||||
digitalWrite(rotMot2Pin, LOW);
|
|
||||||
}
|
|
||||||
analogWrite(vitMot1Pin, 150);
|
|
||||||
analogWrite(vitMot2Pin, 150);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Remerciements
|
|
||||||
|
|
||||||
Merci au Fablab de l’ENSEIRB-MATMECA, Eirlab, pour nous avoir permis de
|
|
||||||
travailler sur ce projet et d’avoir contribué matériellement au projet.
|
|
||||||
|
|
||||||
Merci aussi aux différents Fabmanagers qui nous ont aidés tout au long du projet
|
|
||||||
en répondant à nos questions plus ou moins posées sous l’emprise de la fatigue.
|
|
||||||
|
|
||||||
À la mémoire de Kaitlin Rooke.
|
|
Before Width: | Height: | Size: 553 KiB After Width: | Height: | Size: 553 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
@ -0,0 +1,294 @@
|
|||||||
|
+++
|
||||||
|
title = "Shy bot – the robot which is scared of the light"
|
||||||
|
description = "Creating a shy robot for a school project."
|
||||||
|
date = 2022-05-19
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["maker", "fablab", "robotics"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
Creating a shy robot for a university project.
|
||||||
|
|
||||||
|
> This is a translated article of mine, original can be found on
|
||||||
|
[Eirlab](https://www.eirlab.net/2022/05/19/robot-timide-le-robot-qui-fuit-la-lumiere/)
|
||||||
|
|
||||||
|
As part of my computer science engeneering curriculum, I enrolled in an option called
|
||||||
|
"Makers" during my fourth year. From this course within
|
||||||
|
[ENSEIRB-MATMECA's makerlab](https://www.eirlab.net)
|
||||||
|
is born a shy robot, one that hates light and loves the shadows.
|
||||||
|
|
||||||
|
This article is all about explaining how it works and how you could reproduce our work, in the
|
||||||
|
most "maker" spirit.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
![A 3/4th view of said robot](images/robot_timide_header.jpg)
|
||||||
|
|
||||||
|
## Base concept
|
||||||
|
|
||||||
|
We wanted to create a robot which would be able to detect, in real time,
|
||||||
|
the brightness of its surroundings, and would then use it to go in the
|
||||||
|
darkest place of the room it would be in, in order to hide (it's a shy one, remember?).
|
||||||
|
|
||||||
|
Instead of going to the darkest place it can detect, there's also another mode
|
||||||
|
available, where instead of moving towards the darkest place, it tries to
|
||||||
|
flee the brightest direction. Although looking similar, these two ways of thinking
|
||||||
|
yield to different results.
|
||||||
|
|
||||||
|
![A view of the robot in its final form.](images/robot_timide_enseirb.jpg)
|
||||||
|
|
||||||
|
## Some hardware
|
||||||
|
|
||||||
|
Before working on the software part of this project, we needed to have some hardware
|
||||||
|
to run our code on.
|
||||||
|
|
||||||
|
It should be noted that all of the components required for this project were
|
||||||
|
supplied by the fablab. We tried to only use parts that were already available,
|
||||||
|
without the need to spend a single euro for this project.
|
||||||
|
|
||||||
|
### Shopping list
|
||||||
|
|
||||||
|
In order to recreate this project, you would need these components:
|
||||||
|
|
||||||
|
- 1x Arduino Uno (or an equivalent clone) – it's the brains of this whole project
|
||||||
|
- 1x Arduino Motor Shield – to control the motors that will move the robot
|
||||||
|
- 2x DC motors (here FT DC 130D) – to actually move the robot
|
||||||
|
- 3x ultrasonic distance sensors HC-SR04 – to avoid hitting walls too often
|
||||||
|
- 4x photoresistors – one per side, to be able to detect brightness around it
|
||||||
|
- 4x 2kΩ resistors – for the photocell circuit, to have a better detection amplitude
|
||||||
|
- 1x 9V battery – because it needs power to do anything
|
||||||
|
- 1x box for 9V batteries [optional] – so the battery is safely mounted
|
||||||
|
|
||||||
|
### Chassis
|
||||||
|
|
||||||
|
The chassis for this robot is a piece of plywood, 5mm thick to be precise. It
|
||||||
|
has been machined using a laser cutter, availabe in the fablab. This plank
|
||||||
|
is about 17 by 15.5 centimeters, and has two beveled corners, which are useful
|
||||||
|
so the robot can see in front of it. The bevels have a 20 degrees angle, going on
|
||||||
|
4.5cm.
|
||||||
|
|
||||||
|
The base plate is then pierced to accomodate for the different components that
|
||||||
|
will need to be mounted on the said plate. The lighter components will be pressure
|
||||||
|
mounted, using a u-shape that will fit around the plate's side.
|
||||||
|
|
||||||
|
![The u-shape to pressure-mount the component on the plate.](images/u_shape.jpg)
|
||||||
|
|
||||||
|
### Power
|
||||||
|
|
||||||
|
For powering the robot, we want to send 9 volts, from a battery or a power supply,
|
||||||
|
in the power input `VIN` of our Arduino board. This tension will then be regulated
|
||||||
|
on the PCB to create the different power rails that would be needed. This input is
|
||||||
|
connected internally to the barrel jack of the Arduino, which can take power ranging
|
||||||
|
from 5 to 9 volts.
|
||||||
|
|
||||||
|
In this project, we are using a 9V battery which is placed in a case that is bolted
|
||||||
|
on the base plate.
|
||||||
|
|
||||||
|
![A picture of the rechargeable 9 volts battery that we are using.](images/pile9v.jpg)
|
||||||
|
|
||||||
|
### Motors
|
||||||
|
|
||||||
|
The motors are bolted to the chassis in this project, because when they were
|
||||||
|
mounted just using pressure, they would run away each time the robot made a
|
||||||
|
rough acceleration.
|
||||||
|
|
||||||
|
These motors are directly powered and controlled using the Arduino Motor Shield,
|
||||||
|
which lets us select a speed ranging from `0` to `255` using a coding API.
|
||||||
|
This API also lets us engage brakes or set the direction in which the wheels
|
||||||
|
are turning.
|
||||||
|
|
||||||
|
The wheels for this project have been 3D printed and pressure-fit on the motors
|
||||||
|
themselves, and an O-ring seal is then used to add some grip.
|
||||||
|
|
||||||
|
### PCB
|
||||||
|
|
||||||
|
In the beginning of this project, we used a breadboard for electronics prototyping.
|
||||||
|
We had a lot of disconnect issues, so as soon as the hardware part could be frozen,
|
||||||
|
we migrated the breadboard electronics to a hand-made PCB using a prototyping board.
|
||||||
|
This made the robot way more resilient to a sharp acceleration curve.
|
||||||
|
|
||||||
|
![A top-view of our hand-made PCB.](images/pcb_irl.jpg)
|
||||||
|
|
||||||
|
On an electronic level, the PCB is routed such as:
|
||||||
|
|
||||||
|
![The PCB routing on a prototyping breadboard.](images/pcb_fritzing.png)
|
||||||
|
|
||||||
|
The different components are interfaced with the PCB using a kind of connector that
|
||||||
|
lets us connect and disconnect things as we iterated on the sensors without having to
|
||||||
|
use a soldering iron.
|
||||||
|
|
||||||
|
## And finally some software
|
||||||
|
|
||||||
|
From a software point-of-view, the project is quite simple.
|
||||||
|
|
||||||
|
On a high-level, the algorithm could be summed up as follows: check all four photocells,
|
||||||
|
and move, using rotation and translation, towards the one that has the darkest value, if
|
||||||
|
`followDark` is set to `true`. If this value is set to `false`, then the robot will move
|
||||||
|
itself in the opposite direction to this brighest value.
|
||||||
|
|
||||||
|
A distance mesurement is made using the three ultrasonic distance sensor, so the robot is
|
||||||
|
able to stop before hitting a wall (hopefully). These sensors act as "kill switches" that
|
||||||
|
forbid the robot from ever moving while these sensors are detecting an obstacle.
|
||||||
|
|
||||||
|
The code is as follow:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define echo1Pin 5
|
||||||
|
#define trig1Pin 4
|
||||||
|
#define echo2Pin 7
|
||||||
|
#define trig2Pin 6
|
||||||
|
#define echo3Pin 10
|
||||||
|
#define trig3Pin 2
|
||||||
|
#define rotMot1Pin 12
|
||||||
|
#define brakeMot1Pin 9
|
||||||
|
#define vitMot1Pin 3
|
||||||
|
#define rotMot2Pin 13
|
||||||
|
#define brakeMot2Pin 8
|
||||||
|
#define vitMot2Pin 11
|
||||||
|
#define photoRes1Pin A2
|
||||||
|
#define photoRes2Pin A3
|
||||||
|
#define photoRes3Pin A4
|
||||||
|
#define photoRes4Pin A5
|
||||||
|
|
||||||
|
#define followDark false
|
||||||
|
|
||||||
|
long duration;
|
||||||
|
int distance;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
pinMode(rotMot1Pin, OUTPUT);
|
||||||
|
pinMode(brakeMot1Pin, OUTPUT);
|
||||||
|
pinMode(vitMot1Pin, OUTPUT);
|
||||||
|
|
||||||
|
pinMode(rotMot2Pin, OUTPUT);
|
||||||
|
pinMode(brakeMot2Pin, OUTPUT);
|
||||||
|
pinMode(vitMot2Pin, OUTPUT);
|
||||||
|
|
||||||
|
pinMode(photoRes1Pin, INPUT);
|
||||||
|
pinMode(photoRes2Pin, INPUT);
|
||||||
|
pinMode(photoRes3Pin, INPUT);
|
||||||
|
pinMode(photoRes4Pin, INPUT);
|
||||||
|
|
||||||
|
pinMode(trig1Pin, OUTPUT);
|
||||||
|
pinMode(echo1Pin, INPUT);
|
||||||
|
pinMode(trig2Pin, OUTPUT);
|
||||||
|
pinMode(echo2Pin, INPUT);
|
||||||
|
pinMode(trig3Pin, OUTPUT);
|
||||||
|
pinMode(echo3Pin, INPUT);
|
||||||
|
|
||||||
|
delay(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
byte echoPins[3] = {echo1Pin,echo2Pin,echo3Pin};
|
||||||
|
byte trigPins[3] = {trig1Pin,trig2Pin,trig3Pin};
|
||||||
|
long int durations[3] = {};
|
||||||
|
long int distances[3] = {};
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++){
|
||||||
|
digitalWrite(trigPins[i], LOW);
|
||||||
|
delayMicroseconds(2);
|
||||||
|
|
||||||
|
digitalWrite(trigPins[i], HIGH);
|
||||||
|
delayMicroseconds(10);
|
||||||
|
digitalWrite(trigPins[i], LOW);
|
||||||
|
durations[i] = pulseIn(echoPins[i], HIGH);
|
||||||
|
distances[i] = durations[i] * 0.034 / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int photoRes[4] = {};
|
||||||
|
Serial.print("Capteur infra : 1[");
|
||||||
|
photoRes[0] = analogRead(photoRes1Pin);
|
||||||
|
Serial.print(photoRes[0]);
|
||||||
|
Serial.print("] 2[");
|
||||||
|
photoRes[1] = analogRead(photoRes2Pin);
|
||||||
|
Serial.print(photoRes[1]);
|
||||||
|
Serial.print("] 3[");
|
||||||
|
photoRes[2] = analogRead(photoRes3Pin);
|
||||||
|
Serial.print(photoRes[2]);
|
||||||
|
Serial.print("] 4[");
|
||||||
|
photoRes[3] = analogRead(photoRes4Pin);
|
||||||
|
Serial.print(photoRes[3]);
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++){
|
||||||
|
Serial.print("] - distance(cm) : ");
|
||||||
|
Serial.print(distances[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
bool b = false;
|
||||||
|
for(int i = 0; i < 3; i++){
|
||||||
|
if (distances[i] < 30) {
|
||||||
|
b= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int photoResRef = photoRes[0];
|
||||||
|
bool photoStop = true;
|
||||||
|
int photoResMinPos = 0;
|
||||||
|
|
||||||
|
if (followDark){
|
||||||
|
int photoResMin = 1024;
|
||||||
|
for (int i = 0; i < 4; i++){
|
||||||
|
if (photoResMin > photoRes[i]){
|
||||||
|
photoResMin = photoRes[i];
|
||||||
|
photoResMinPos = i;
|
||||||
|
}
|
||||||
|
if (abs(photoResRef - photoRes[i]) > 20){
|
||||||
|
photoStop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
int photoResMax = 0;
|
||||||
|
for (int i = 0; i < 4; i++){
|
||||||
|
if (photoResMax < photoRes[i]){
|
||||||
|
photoResMax = photoRes[i];
|
||||||
|
photoResMinPos = (i + 2)%4;
|
||||||
|
}
|
||||||
|
if (abs(photoResRef - photoRes[i]) > 20){
|
||||||
|
photoStop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(photoResMinPos);
|
||||||
|
|
||||||
|
if (b || photoStop){
|
||||||
|
digitalWrite(brakeMot1Pin, HIGH);
|
||||||
|
digitalWrite(brakeMot2Pin, HIGH);
|
||||||
|
} else {
|
||||||
|
digitalWrite(brakeMot1Pin, LOW);
|
||||||
|
digitalWrite(brakeMot2Pin, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (photoResMinPos == 0){
|
||||||
|
digitalWrite(rotMot1Pin, LOW);
|
||||||
|
digitalWrite(rotMot2Pin, LOW);
|
||||||
|
}
|
||||||
|
if (photoResMinPos == 1){
|
||||||
|
digitalWrite(rotMot1Pin, LOW);
|
||||||
|
digitalWrite(rotMot2Pin, LOW);
|
||||||
|
}
|
||||||
|
if (photoResMinPos == 2){
|
||||||
|
digitalWrite(rotMot1Pin, HIGH);
|
||||||
|
digitalWrite(rotMot2Pin, HIGH);
|
||||||
|
}
|
||||||
|
if (photoResMinPos == 3){
|
||||||
|
digitalWrite(rotMot1Pin, HIGH);
|
||||||
|
digitalWrite(rotMot2Pin, LOW);
|
||||||
|
}
|
||||||
|
analogWrite(vitMot1Pin, 150);
|
||||||
|
analogWrite(vitMot2Pin, 150);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
We wanted to say thank you to the ENSEIRB-MATMECA, to our fablab, EIRLAB, for
|
||||||
|
trusting us, helping us and providing the hardware we used throughout this project.
|
||||||
|
|
||||||
|
Thank you also to the fab-managers who helped us and answered all of our questions,
|
||||||
|
more or less asked under fatigue.
|
||||||
|
|
||||||
|
In memory of Kaitlin Rooke.
|
@ -1,5 +1,5 @@
|
|||||||
+++
|
+++
|
||||||
title = "Articles archivés"
|
title = "Archived articles"
|
||||||
path = "archive"
|
path = "archive"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<h1 class="post-title">{% block heading %}Perdu ?{% endblock heading %}</h1>
|
<h1 class="post-title">{% block heading %}Lost?{% endblock heading %}</h1>
|
||||||
<p>{% block message %}Cette page n'existe pas.{% endblock message %}</p>
|
<p>{% block message %}Looks like you're trying something that doesn't exist (anymore?).{% endblock message %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -24,14 +24,14 @@
|
|||||||
<span class="button previous">
|
<span class="button previous">
|
||||||
<a href="{{ paginator.previous | safe }}">
|
<a href="{{ paginator.previous | safe }}">
|
||||||
<span class="button__icon">←</span>
|
<span class="button__icon">←</span>
|
||||||
<span class="button__text">Posts plus récents</span>
|
<span class="button__text">More recent posts</span>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{%- if paginator.next %}
|
{%- if paginator.next %}
|
||||||
<span class="button next">
|
<span class="button next">
|
||||||
<a href="{{ paginator.next | safe }}">
|
<a href="{{ paginator.next | safe }}">
|
||||||
<span class="button__text">Posts plus anciens</span>
|
<span class="button__text">Older posts</span>
|
||||||
<span class="button__icon">→</span>
|
<span class="button__icon">→</span>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
{% macro content(page, summary, show_only_description) %}
|
|
||||||
{%- if show_only_description %}
|
|
||||||
<div class="post-content">
|
|
||||||
{{ page.description | safe }}
|
|
||||||
</div>
|
|
||||||
{% elif summary and page.summary %}
|
|
||||||
<div class="post-content">
|
|
||||||
{{ page.summary | safe }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- ︎ -- force text style - some devices render this as emoji -->
|
|
||||||
<a class="read-more button" href="{{ page.permalink | safe }}">
|
|
||||||
<span class="button__text">En savoir plus</span>
|
|
||||||
<span class="button__icon">↩︎</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{#- full content -#}
|
|
||||||
<div class="post-content">
|
|
||||||
{{ page.content | safe }}
|
|
||||||
</div>
|
|
||||||
{%- endif %}
|
|
||||||
{% endmacro content %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro date(page) %}
|
|
||||||
<span class="post-date">
|
|
||||||
{%- if page.date %}
|
|
||||||
{{ page.date | date(format="%d-%m-%Y") }}
|
|
||||||
{% endif -%}
|
|
||||||
</span>
|
|
||||||
{% endmacro post_date %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro earlier_later(page) %}
|
|
||||||
{%- if config.extra.enable_post_view_navigation and page.lower or page.higher %}
|
|
||||||
<div class="pagination">
|
|
||||||
<div class="pagination__title">
|
|
||||||
<span class="pagination__title-h">{{ config.extra.post_view_navigation_prompt }}</span>
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
<div class="pagination__buttons">
|
|
||||||
{%- if page.higher %}
|
|
||||||
<span class="button previous">
|
|
||||||
<a href="{{ page.higher.permalink | safe }}">
|
|
||||||
<span class="button__icon">←</span>
|
|
||||||
<span class="button__text">{{ page.higher.title }}</span>
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if page.lower %}
|
|
||||||
<span class="button next">
|
|
||||||
<a href="{{ page.lower.permalink | safe }}">
|
|
||||||
<span class="button__text">{{ page.lower.title }}</span>
|
|
||||||
<span class="button__icon">→</span>
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
{% endif -%}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif -%}
|
|
||||||
{% endmacro earlier_later %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro header(page) %}
|
|
||||||
<h1 class="post-title"><a href="{{ page.permalink | safe }}">{{ page.title }}</a></h1>
|
|
||||||
<div class="post-meta-inline">
|
|
||||||
{{ post_macros::date(page=page) }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ post_macros::tags(page=page) }}
|
|
||||||
{% endmacro header %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro list_posts(pages) %}
|
|
||||||
<ul>
|
|
||||||
{%- for page in pages %}
|
|
||||||
{%- if page.draft %}
|
|
||||||
{% continue %}
|
|
||||||
{% endif -%}
|
|
||||||
<li class="post-list">
|
|
||||||
<a href="{{ page.permalink | safe }}">
|
|
||||||
{{ post_macros::date(page=page) }}
|
|
||||||
:: <span class="post-list-title">{{ page.title }}</span></a>
|
|
||||||
{{ post_macros::tags(page=page, short=true) }}
|
|
||||||
</li>
|
|
||||||
{% endfor -%}
|
|
||||||
</ul>
|
|
||||||
{% endmacro list_posts %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro tags(page, short=false) %}
|
|
||||||
{%- if page.taxonomies and page.taxonomies.tags %}
|
|
||||||
<span class="post-tags-inline">
|
|
||||||
{%- if short %}
|
|
||||||
::
|
|
||||||
{%- set sep = "," -%}
|
|
||||||
{% else %}
|
|
||||||
:: tags:
|
|
||||||
{%- set sep = " " -%}
|
|
||||||
{% endif -%}
|
|
||||||
{%- for tag in page.taxonomies.tags | sort | unique(case_sensitive=false) %}
|
|
||||||
<a class="post-tag" href="{{ get_taxonomy_url(kind='tags', name=tag) | safe }}">#{{ tag }}</a>
|
|
||||||
{%- if not loop.last %}{{ sep | safe }}{% endif -%}
|
|
||||||
{% endfor -%}
|
|
||||||
</span>
|
|
||||||
{% endif -%}
|
|
||||||
{% endmacro tags %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends "terminimal/templates/tags/list.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="post">
|
|
||||||
<h1 class="post-title">Liste des tags</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for term in terms %}
|
|
||||||
<li class="tag-list">
|
|
||||||
<a href="{{ term.permalink | safe }}">
|
|
||||||
{{ term.name }} ({{ term.pages | length }} post{{ term.pages | length | pluralize }})
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends "terminimal/templates/tags/single.html" %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="post">
|
|
||||||
<h1 class="post-title">
|
|
||||||
tag: #{{ term.name }}
|
|
||||||
({{ term.pages | length }} post{{ term.pages | length | pluralize }})
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<a href="{{ config.base_url | safe }}/tags">
|
|
||||||
Voir tous les tags
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{{ post_macros::list_posts(pages=term.pages) }}
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|