SVG to OLED ?
L'idée
Les écrans OLED permettent d'afficher des pixels, lignes, symboles dans un format simple, typiquement à partir des coordonnées des points (x,y).
De l'autre côté, un outil tel que Inkscape permet de créer facilement des fichiers SVG simples, fournissant des formes notamment au format path() qui donne accès facilement aux points unitaires d'un chemin donné. Les possibilités de Inkscape permettent notamment d'extraire les chemins à partir de n'importe quel texte, logo, symbole, etc... notamment à partir d'une image.
Du coup, il vient logiquement en tête l'idée de profiter des possibilités de Inkscape et du format SVG pour réaliser des affichages sur un écran OLED. On va explorer ici ce qui est facilement possible et faisable.
Note
Les concepts vu ici sont les mêmes que ceux qui sont utiles pour l'extraction des points d'une forme pour la découpe numérique. La démarche est donc doublement intéressante à maîtriser car bien qu'appliquée en premier lieu ici pour un écran OLED, elle pourra tout ) fait servir pour des applications de codages en découpe numérique 2D. Bref, vous ne perdez pas votre temps...
Créer un fichier SVG
Ouvrir Inkscape et créer un rectangle aux dimensions de l'écran en pixels, et y placer un premier chemin, un rectangle, à basculer en path.
Extraire les coordonnées du path à partir du SVG
La première étape se fait sur l'ordinateur, dans l'interpréteur Python.
On va utiliser une librairie Python 3 dédiée à la gestion des fichiers SVG.
Installer la librairie svgpathtools
Nous présentons cette librairie par ailleurs. Pour mémoire pour installer la librairie svgpathtools, on commence par installer 2 dépendances :
sudo pip3 install svgwrite
sudo pip3 install numpy
Puis la lib à proprement parler :
sudo pip3 uninstall svgpathtools
Ouvrir un fichier SVG et extraire les points d'un chemin path
Une fois fait, ouvrir un interpréteur Python dans le répertoire
jupyter qtconsole
Ensuite, on fait :
import svgpathtools as spt
import numpy as np
paths,attributes=spt.svg2paths("dessin.svg")
paths
Out[18]:
[Path(Line(start=(138.993804932+21.8540344238j), end=(316.159164429+21.8540344238j)),
Line(start=(316.159164429+21.8540344238j), end=(316.159164429+92.7201766968j)),
Line(start=(316.159164429+92.7201766968j), end=(138.993804932+92.7201766968j)),
Line(start=(138.993804932+92.7201766968j), end=(138.993804932+21.8540344238j)))]
On voit qu'on obtient le path sous forme d'objets unitaires Line
. Ce que l'on veut, c'est extraire les points, et pour cela on pourra créer une petite fonction de la forme :
def pathToNumpy(pathIn):
points=[] # crée un list vide
for elem in pathIn: # défile les éléments du Path source
#print ((elem.point(0).real, elem.point(0).imag))
points.append((elem.point(0).real, elem.point(0).imag)) # tuple du point start à partir coord complexes
#points.append((elem.point(1).real, elem.point(1).imag)) # tuple du point end
# utiliser que le point start pour ne pas avoir de doublons...
points.append((pathIn[-1].point(1).real, pathIn[-1].point(1).imag) ) # ajout du end du dernier point pour fermer
#print (points)
points=np.asarray(points)
return points
Une fois fait, il est facile de récupérer les points sous forme d'un tableau numpy :
points=pathToNumpy(paths[0])
points
Out[26]:
array([[138.99380493, 21.85403442],
[316.15916443, 21.85403442],
[316.15916443, 92.7201767 ],
[138.99380493, 92.7201767 ],
[138.99380493, 21.85403442]])
Micropython, ne supportant pas les tableau Numpy, il est préférable de récupérer un list
ce qui donne :
points.tolist()
Out[27]:
[[138.993804932, 21.8540344238],
[316.159164429, 21.8540344238],
[316.159164429, 92.7201766968],
[138.993804932, 92.7201766968],
[138.993804932, 21.8540344238]]
```
### Afficher un chemin sur l'écran OLED
A présent, on va pouvoir afficher le résultat sur l'écran OLED. On se place ici dans le cas d'un écran 128x32 utilisé avec Pi Pico.
On commence par initialiser le OLED :
```python
from machine import I2C, Pin
import ssd1306
i2c = I2C(1,scl=Pin(19), sda=Pin(18)) # port i2C1 - broches variables selon carte utilisée
oled = ssd1306.SSD1306_I2C(128, 32, i2c, 0x3c)
On intègre notre list obtenu précédemment dans le code Micropython :
x,y=0,1
points=[[39.2271404006, 6.1676941431],
[89.2271417281, 6.1676941431],
[89.2271417281, 26.1676942434],
[39.2271404006, 26.1676942434],
[39.2271404006, 6.1676941431]]
```
A présent, nous avons besoin d'une fonction pour afficher les points :
```python
def path(points):
for point in points:
oled.pixel(int(point[x]), int(point[y]),1)
```
Et d'une autre pour afficher les lignes à partir des points :
```python
def path_lines(points):
for idx,point in enumerate(points[:-1]):
oled.line(
int(points[idx][x]), int(points[idx][y]),
int(points[idx+1][x]), int(points[idx+1][y]),
1)
```
Le résultat s'affiche avec :
```python
#path(points)
path_lines(points)
oled.show()
Yes ! Tout est OK.
Afficher plusieurs chemins
Une fois qu'on sait le faire pour un chemin, on sait le faire pour plusieurs. Il est dès lors assez facile d'y arriver.
Compléter le SVG avec un autre objet en chemin. Cette fois, on récupère les points en faisant :
paths,attributes=spt.svg2paths("dessin.svg")
pointsPath=[]
for path in paths:
pointsPath.append(pathToNumpy(path).tolist())
pointsPath
Out[34]:
[[[39.2271404006, 6.1676941431],
[89.2271417281, 6.1676941431],
[89.2271417281, 26.1676942434],
[39.2271404006, 26.1676942434],
[39.2271404006, 6.1676941431]],
[[44.2271404006, 11.167694143],
[84.2271417281, 11.167694143],
[84.2271417281, 21.1676942433],
[44.2271404006, 21.1676942433],
[44.2271404006, 11.167694143]]]
On obtient un list de list, un "lol"...
A présent, on peut facilement modifier / compléter le code micropython en faisant :
pointsPath=[[[39.2271404006, 6.1676941431],
[89.2271417281, 6.1676941431],
[89.2271417281, 26.1676942434],
[39.2271404006, 26.1676942434],
[39.2271404006, 6.1676941431]],
[[44.2271404006, 11.167694143],
[84.2271417281, 11.167694143],
[84.2271417281, 21.1676942433],
[44.2271404006, 21.1676942433],
[44.2271404006, 11.167694143]]]
for points in pointsPath:
path_lines(points)
oled.show()
Yes ! That'is it !
Y'a plus qu'à faire mumuse...
Une fois arrivé là, on peut vite s'amuser, car à l'identique, on va pouvoir utiliser des polices, des symboles, etc.
Inkscape peut dès lors devenir une interface de conception d'interface pour écran OLED... Il suffit de prévoir les emplacements où on réalisera de l'affichage dynamique, texte ou points. Et pour le reste, çà peut être fait à partir de Inkscape.
Améliorations
- obtenir directement un tableau de points au format int pour éviter d'avoir à le faire dans le code Micropython
def pathToInt(pathIn):
points=[] # crée un list vide
for elem in pathIn: # défile les éléments du Path source
#print ((elem.point(0).real, elem.point(0).imag))
points.append((int(elem.point(0).real), int(elem.point(0).imag))) # tuple du point start à partir coord complexes
#points.append((elem.point(1).real, elem.point(1).imag)) # tuple du point end
# utiliser que le point start pour ne pas avoir de doublons...
points.append((int(pathIn[-1].point(1).real), int(pathIn[-1].point(1).imag)) ) # ajout du end du dernier point pour fermer
#print (points)
points=np.asarray(points)
return points
- Ecrire un script Python capable d'automatiquement fournir un fichier *.py contenant la list de points utiles, fichier qui sera copiable sur la carte Flash et importabla facilement dans un code utilisant un écran.