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.