30/10/2024
202410300800 iot prises meross python
Alors que je baguenaudais sur un site de vente en ligne à une période propice à moultes réductions et dans un énième moment d'égarement j'ai fait l'acquisition de "prises connectées". Par 'égarement' j'entends qu'aucune recherche préalable de compatibilité avec mon environnement n'a été faite et que le besoin à ce moment était loin d'être justifié. Bref, j'ai craqué.
Le matos
Lot de 2 prises Meross MSS305ZF Smart Wi-Fi Plug 16A
Première impression
Autant le dire tout de suite: port série, Tasmota, firmware alternatif et autres bidouilleries ne seront pas de la partie. Et comme ce genre de bestiole s'accompagne toujours d'une application pour débilophone, l'oubli au fond du tiroir 'okazou' semblait être la destinée finale de cet achat compulsif. Après quelques (tardives) recherches, je m'aperçois que tout n'est pas perdu.
Configuration initiale
Je ne suis pas spécialiste de ce genre de gadget mais la séquence suivante me semble la façon usuelle de procéder:
- installer une application sur le débilophone
- dire au revoir à sa vie privée
- brancher l'appareil
- passer l'appareil en mode "comment c'est-y que j'me connecte à la wifi pour tout balancer dans le claoude" (sic)
- pousser l'accès au réseau sans fil à l'aide de l'application
- dire au revoir à sa vie privée (bis)
Problème: mon débilophone est un Samsung S9 sous LineageOS (ce qui le rend tout de suite un peu moins con) qui n'a pas accès aux magasins d'applications.
Solution: Grâces soient rendues à Fabi019 et l'apk qu'il met à disposition depuis https://github.com/Fabi019/MerossBLE et qui s'installe sans problème. 2 minutes plus tard, une première prise est connectée. Le truc à retenir qui servira plus tard: la valeur du champ 'Key' du cartouche '1. Server Configuration' .
Faire mumuse
Je n'ai pas (encore ?) de solution domotique, j'ai vaguement lancé un serveur mqtt (la prise envoie bien des données dessus) mais ce qui m'intérresse c'est d'interagir directement avec la prise. Pour se faire j'ai trouvé des projets en go, nodejs et python. Comme chacun sait, le plus important c'est la doc. Et la doc, pour mon cas, elle est là: https://github.com/arandall/meross/blob/main/doc/protocol.md .
Le protocole n'est pas très compliqué et consiste à l'envoi d'un JSON à une url fixe. J'ai longtemps buté sur un élément:
sign == Signing value equal to md5(messageId + key + timestamp)
car, de ce que j'avais lu, cette 'key' semblait être fournie après l'ouverture d'un compte chez meross. J'ai finalement compris que je pouvais fixer cette valeur depuis l'application du débilophone, comme vu un peu plus haut (le truc à retenir).
Je me suis fendu de quelques lignes de python (car on n'est jamais aussi bien servi que par soi-même et non c'est pas du nih) et je peux désormais contrôler mes prises depuis la VM FreeBSD de ma Freebox Delta.
Reste à faire
La prise en mode "appairage" propose un point d'accès wifi à partir duquel il est possible de pousser la configuration. Je n'ai pas encore testé cette possibilité. Le point d'accès auquel se connectent les prises ne propose pas d'ipv6 et je ne trouve pas trace des adresses MAC des prises dans mon voisinage ipv6. Je pense qu'on peut faire des trucs rigolos avec les '.TriggerX' . Quant à 'Appliance.Control.Upgrade', si bucket-meross-static.meross.com existe, je n'ai pas encore trouvé le moyen d'obtenir le chemin vers une éventuelle mise à jour du firmware.
Conclusion
Est-ce que je regrette mon achat ? Non. Est-ce que je vais surveiller ma consommation électrique afin de la faire baisser ? Je ne crois pas. Est-ce que je vais faire du mqtt ? Je ne sais pas. Est-ce que je vais finir par installer une solution de domotique ? J'en doute mais si j'ai bien compris c'est un engrenage. Et comme j'ai aussi 2 prises 'Amazon smart plug', le mal est peut-être déjà fait.
Commentaires: https://github.com/bsdsx/blog_posts/issues/21
29/02/2024
202402290800 bhyve wifi openwrt
J'ai pendant très longtemps utilisé un mini routeur sous OpenWrt car le wifi de mon ancien nuc n'était ni stable ni performant. Les ressources de ma nouvelle machine de travail (AMD Ryzen 7 3750H) étant bien supérieures, l'idée m'est venue de mimer FreeBSD wifibox à la sauce OpenWrt. Un portable Lenovo T450 en a aussi fait les frais.
wifibox
Le principe est assez simple: si la plateforme matérielle le permet, attribuer le périphérique wifi à une machine virtuelle dont le pilote a une meilleure prise en charge que celui de FreeBSD. Le projet a fait le choix l'Alpine Linux.
wifi et bhyve
Je commence par identifier le périphérique:
$ pciconf -lv ... iwm0@pci0:2:0:0: class=0x028000 rev=0x59 hdr=0x00 vendor=0x8086 device=0x095a subvendor=0x8086 subdevice=0x5010 vendor = 'Intel Corporation' device = 'Wireless 7265' class = network ...
Il ne doit plus être pris en charge par le pilote iwm mais par le Pci PassThru (ppt). Je dois aussi charger le module vmm et parce que j'ai un processeur AMD activer un sysctl comme indiqué sur le wiki FreeBSD)
$ cat /boot/loader.conf.d/vmm.conf vmm_load="YES" pptdevs="2/0/0" # à adapter suivant pciconf -lv hw.vmm.amdvi.enable=1
Un redémarrage plus tard, on obtient:
$ pciconf -lv ... ppt0@pci0:2:0:0: class=0x028000 rev=0x59 hdr=0x00 vendor=0x8086 device=0x095a subvendor=0x8086 subdevice=0x5010 vendor = 'Intel Corporation' device = 'Wireless 7265' class = network ...
Machine virtuelle
Je récupère la dernière image d'OpenWrt:
$ fetch https://downloads.openwrt.org/releases/23.05.2/targets/x86/64/openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz $ gunzip openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz gunzip: openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz: trailing garbage ignored
Pour que bhyve puisse utiliser une image efi je dois ajouter le paquet suivant:
$ doas pkg install bhyve-firmware
et je vérifie que la machine virtuelle démarre bien:
$ doas bhyve -H -P -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0
Un halt plus tard je peux détruire cette machine virtuelle:
$ doas bhyvectl --destroy --vm=vm0
Utile quand on joue au plus malin et qu'avec 128 Mo de mémoire on obtient:
error: out of memory. error: out of memory. Press any key to continue...Press any key to continue...
OpenWrt
Léger problème avec l'image fournie: elle ne contient absolument rien (module, pilote, outil ...) concernant le wifi (c'est voulu par le projet pour la plate-forme x86). Je prépare un disque à ajouter à mon image avec le contenu nécessaire:
$ truncate -s 5M pkgs.img # oui, 5 Mo c'est bien assez $ newfs_msdos ./pkgs.img newfs_msdos: warning, ./pkgs.img is not a character device ./pkgs.img: 10192 sectors in 1274 FAT12 clusters (4096 bytes/cluster) BytesPerSec=512 SecPerClust=8 ResSectors=1 FATs=2 RootDirEnts=512 Sectors=10240 Media=0xf0 FATsecs=4 SecPerTrack=63 Heads=255 HiddenSecs=0 $ doas mdconfig ./pkgs.img md0 $ doas mount -t msdosfs /dev/md0 /media
Depuis https://downloads.openwrt.org/releases/23.05.2/packages/x86_64/base/ je récupère les paquets:
hostapd-common_2023-09-08-e5ccbfc6-6_x86_64.ipk iw_4.14-1_x86_64.ipk iwinfo_2023-07-01-ca79f641-1_x86_64.ipk iwlwifi-firmware-iwl7265d_20230804-1_x86_64.ipk libnl-tiny1_2023-07-27-bc92a280-1_x86_64.ipk ucode-mod-nl80211_2023-11-07-a6e75e02-1_x86_64.ipk ucode-mod-rtnl_2023-11-07-a6e75e02-1_x86_64.ipk ucode-mod-uloop_2023-11-07-a6e75e02-1_x86_64.ipk wireless-regdb_2024.01.23-1_all.ipk wpa-cli_2023-09-08-e5ccbfc6-6_x86_64.ipk wpa-supplicant-mini_2023-09-08-e5ccbfc6-6_x86_64.ipk
et depuis https://downloads.openwrt.org/releases/23.05.2/targets/x86/64/packages/ les modules:
kmod-cfg80211_5.15.137+6.1.24-3_x86_64.ipk kmod-crypto-aead_5.15.137-1_x86_64.ipk kmod-crypto-cbc_5.15.137-1_x86_64.ipk kmod-crypto-ccm_5.15.137-1_x86_64.ipk kmod-crypto-cmac_5.15.137-1_x86_64.ipk kmod-crypto-ctr_5.15.137-1_x86_64.ipk kmod-crypto-gcm_5.15.137-1_x86_64.ipk kmod-crypto-gf128_5.15.137-1_x86_64.ipk kmod-crypto-ghash_5.15.137-1_x86_64.ipk kmod-crypto-hmac_5.15.137-1_x86_64.ipk kmod-crypto-manager_5.15.137-1_x86_64.ipk kmod-crypto-null_5.15.137-1_x86_64.ipk kmod-crypto-rng_5.15.137-1_x86_64.ipk kmod-crypto-seqiv_5.15.137-1_x86_64.ipk kmod-crypto-sha512_5.15.137-1_x86_64.ipk kmod-iwlwifi_5.15.137+6.1.24-3_x86_64.ipk kmod-mac80211-hwsim_5.15.137+6.1.24-3_x86_64.ipk kmod-mac80211_5.15.137+6.1.24-3_x86_64.ipk
Je démarre la machine virtuelle avec ce nouveau disque:
$ doas umount /media $ doas mdconfig -du md0 $ doas bhyve -H -P -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 4:0,virtio-blk,./pkgs.img -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0
et j'installe les paquets:
root@OpenWrt:/# mount -t vfat /dev/vdb /mnt/ root@OpenWrt:/# opkg install /mnt/libnl-tiny1_2023-07-27-bc92a280-1_x86_64.ipk Package libnl-tiny1 (2023-07-27-bc92a280-1) installed in root is up to date. root@OpenWrt:/# ln /usr/lib/libnl-tiny.so.1 /usr/lib/libnl-tiny.so # sinon iw va râler root@OpenWrt:/# opkg install /mnt/hostapd-common_2023-09-08-e5ccbfc6-6_x86_64.ipk /mnt/iw_4.14-1_x86_64.ipk /mnt/iwinfo_2023-07-01-ca79f641-1_x86_64.ipk /mnt/iwlwifi-firmware-iwl7265d_20230804-1_x86_64.ipk /mnt/ucode-mod-* /mnt/wireless-regdb_2024.01.23-1_all.ipk /mnt/wpa-cli_2023-09-08-e5ccbfc6-6_x86_64.ipk /mnt/wpa-supplicant-mini_2023-09-08-e5ccbfc6-6_x86_64.ipk Installing hostapd-common (2023-09-08-e5ccbfc6-6) to root... Installing iw (4.14-1) to root... Installing iwinfo (2023-07-01-ca79f641-1) to root... Installing iwlwifi-firmware-iwl7265d (20230804-1) to root... Installing ucode-mod-nl80211 (2023-11-07-a6e75e02-1) to root... Installing ucode-mod-rtnl (2023-11-07-a6e75e02-1) to root... Installing ucode-mod-uloop (2023-11-07-a6e75e02-1) to root... Installing wireless-regdb (2024.01.23-1) to root... Installing wpa-cli (2023-09-08-e5ccbfc6-6) to root... Installing wpa-supplicant-mini (2023-09-08-e5ccbfc6-6) to root... Configuring iwinfo. Configuring iw. Configuring ucode-mod-uloop. Configuring hostapd-common. Configuring ucode-mod-nl80211. Configuring ucode-mod-rtnl. Configuring wpa-supplicant-mini. Configuring iwlwifi-firmware-iwl7265d. Configuring wpa-cli. Configuring wireless-regdb. root@OpenWrt:/# opkg install /mnt/kmod-* ... root@OpenWrt:/# umount /mnt root@OpenWrt:/# halt
Il est temps d'affecter le périphérique wifi à la machine virtuelle (adapter 2/0/0 au pciconf -lv et ne pas oublier l'option -S):
$ doas bhyve -H -P -S -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 7:0,passthru,2/0/0 -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0
Configuration OpenWrt
Pas besoin du délai de grub:
root@OpenWrt:/# grep timeout /boot/grub/grub.cfg set timeout="0"
Quelques modules sont chargés au démarrage, je ne garde que le minimum (et encore, je pense que seul 30-fs-vfat est utile pour récupérer la configuration de grub):
root@OpenWrt:/# ls -1 /etc/modules-boot.d/ 02-crypto-hash 04-crypto-crc32c 09-crypto-aead 09-crypto-manager 30-fs-vfat
Pareil pour les modules chargés après le démarrage (je doit pourvoir encore en supprimer quelques uns):
root@OpenWrt:/# ls -1 /etc/modules.d/ 02-crypto-hash 04-crypto-crc32c 09-crypto-acompress 09-crypto-aead 09-crypto-cbc 09-crypto-ccm 09-crypto-cmac 09-crypto-ctr 09-crypto-gcm 09-crypto-gf128 09-crypto-ghash 09-crypto-hmac 09-crypto-manager 09-crypto-null 09-crypto-rng 09-crypto-seqiv 09-crypto-sha512 25-nls-cp437 25-nls-iso8859-1 25-nls-utf8 30-fs-vfat iwlwifi lib-crc-ccitt lib-crc32c lib-lzo mac80211-hwsim
Je désactive les services inutiles:
root@OpenWrt:/# /etc/init.d/dnsmasq stop && /etc/init.d/dnsmasq disable root@OpenWrt:/# /etc/init.d/dropbear stop && /etc/init.d/dropbear disable root@OpenWrt:/# /etc/init.d/firewall stop && /etc/init.d/firewall disable root@OpenWrt:/# /etc/init.d/gpio_switch stop && /etc/init.d/gpio_switch disable root@OpenWrt:/# /etc/init.d/led stop && /etc/init.d/led disable root@OpenWrt:/# /etc/init.d/sysntpd stop && /etc/init.d/sysntpd disable root@OpenWrt:/# /etc/init.d/uhttpd stop && /etc/init.d/uhttpd disable
Je configure le wifi:
root@OpenWrt:/# cat /etc/config/wireless ... config wifi-device 'radio2' option type 'mac80211' option path 'pci0000:00/0000:00:07.0' option channel '36' option band '5g' option htmode 'VHT80' option disabled '0' config wifi-iface 'default_radio2' option device 'radio2' option network 'wan wan6' option mode 'sta' option ssid 'mon_super_ssid' option encryption 'psk2' option key 'ma_super_key'
et le réseau:
root@OpenWrt:/# cat /etc/config/network config interface 'loopback' option device 'lo' option proto 'static' option ipaddr '127.0.0.1' option netmask '255.0.0.0' config interface 'lan' option device 'eth0' option proto 'static' option ipaddr '172.20.99.1' option netmask '255.255.255.0' option ip6addr '2db8:xxxx:xxxx:xx3:172:20:99:1/112' config interface 'wan' option proto 'static' option ipaddr '172.20.62.99' option netmask '255.255.255.0' option gateway '172.20.62.1' list dns '172.30.63.20' list dns '172.30.63.10' config interface 'wan6' option proto 'static' option ip6addr '2db8:xxxx:xxxx:xx2::99/64' option ip6gw '2db8:xxxx:xxxx:xx2::1' list dns '2db8:xxxx:xxxx:xx1:172:30:63:20' list dns '2db8:xxxx:xxxx:xx1:172:30:63:10'
Configuration FreeBSD
Le bridge:
$ cat /etc/rc.conf.d/bridge autobridge_interfaces="bridge0" autobridge_bridge0="tap0"
Le réseau:
$ cat /etc/rc.conf.d/network cloned_interfaces="bridge0 tap0" ifconfig_bridge0="inet 172.20.99.10 netmask 255.255.255.0" ifconfig_bridge0_ipv6="inet6 auto_linklocal" ifconfig_bridge0_alias0="inet6 2db8:xxxx:xxxx:xx3:172:20:99:10 prefixlen 112"
$ cat /etc/rc.conf.d/routing defaultrouter="172.20.99.1" ipv6_defaultrouter="2db8:xxxx:xxxx:xx3:172:20:99:1"
L'accès à la console de la machine virtuelle à l'aide de nmdm:
$ cat /etc/rc.conf.d/kld kld_list="/boot/modules/amdgpu.ko nmdm"
Le démarrage de la machine virtuelle:
$ cat /etc/rc.local /usr/sbin/bhyve -H -P -S -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 3:0,virtio-blk,/chemin/complet/vers/openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 7:0,passthru,2/0/0 -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,/dev/nmdm0A vm0 &
L'accès à la console de la machine virtuelle se fera à l'aide de cu:
$ doas cu -l /dev/nmdm0B # oui "B", man 4 nmdm
Mon réseau
La configuration réseau présentée est loin d'être la plus commune (pas de dhcp ni de nat, ipv6 sans autoconf). Pour une version plus "traditionnelle" (dhcp, pas d'ipv6), la machine virtuelle devra:
- faire du dhcp sur l'interface wan
- activer le firewall pour faire du nat
Ce qui devrait peu ou prou correspondre à (pas encore testé):
root@OpenWrt:/# cat /etc/config/network .... config interface 'wan' option proto 'dhcp' root@OpenWrt:/# /etc/init.d/firewall enable && /etc/init.d/firewall start
Au pire la documentation d'OpenWrt https://openwrt.org/docs/guide-user/network/start est plus que fournie.
Pour conclure
Le iperf est sans appel:
$ iperf3 -6 -c proxy ... [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 226 MBytes 190 Mbits/sec 19 sender [ 5] 0.00-10.01 sec 226 MBytes 189 Mbits/sec receiver $ iperf3 -6 -c proxy ... [ 5] 0.00-10.00 sec 263 MBytes 221 Mbits/sec 0 sender [ 5] 0.00-10.02 sec 263 MBytes 220 Mbits/sec receiver
C'est loin d'être parfait au vu de la configuration du réseau:
freebox delta [ vm proxy sous FreeBSD ] <-- rj45 --> ap wifi Mikrotik hAP ac^2 <- -> nuc
mais c'est toujours mieux que la dizaine de Mbits/sec (dont je me contentais très bien) de mon ancienne installation.
Le mini routeur qui trônait fièrement au dessus du nuc devrait finir au fin fond d'un tirroir. Mais comme il possède un lien série, j'en ferais bien un Frankenstein à base de bluetooth ...
Commentaires: https://github.com/bsdsx/blog_posts/issues/20
Correction 2024-03-17
J'ai oublié '-s 2:0,virtio-net,tap0' à chaque commande bhyve.
10/12/2023
202312100800 stm32 openocd
Faire clignoter une LED avec openocd
J'utilise openocd pour communiquer avec mes cartes stm32. N'utilisant ni tcl ni gdb (désolé pour les afficionados valencians j'ai ajouté ces lignes dans mon fichier de configuration:
gdb_port disabled tcl_port disabled
Il ne me reste donc plus que le telnet en écoute sur le port 4444 pour dialoguer avec mes bestioles, ce que je fais à l'aide de nc:
$ echo 'reset halt' | nc -N 127.0.0.1 4444 $ echo "flash write_image erase ${PWD}/main.bin 0x8000000" | nc -N 127.0.0.1 4444 $ echo 'reset run' | nc -N 127.0.0.1 4444
Pris d'une inspiration aussi divine que soudaine j'ai dernièrement tenté un:
$ echo help | nc -N 127.0.0.1 4444
et quelle ne fût pas ma surprise devant cette avalanche de commandes ! Je suis loin d'en avoir fait le tour mais les suivantes ont attiré mon attention:
mdb ['phys'] address [count] display memory bytes mdd ['phys'] address [count] display memory double-words mdh ['phys'] address [count] display memory half-words mdw ['phys'] address [count] display memory words
mwb ['phys'] address value [count] write memory byte mwd ['phys'] address value [count] write memory double-word mwh ['phys'] address value [count] write memory half-word mww ['phys'] address value [count] write memory word
reset [run|halt|init]
Le manuel de référence indique, page 172, que la valeur initiale du Port configuration register high vaut 0x4444 4444. Du fait qu'elle soit facilement reconnaissable, c'est cette valeur que je vais tenter d'afficher. Autre information sur cette page, le décalage à appliquer depuis l'adresse de base du GPIO:
Address offset: 0x04
Page 51, on peut lire que l'adresse de base du GPIO Port C se trouve en 0x4001 1000, le registre de configuration se trouve donc en 0x4001 1004. Je vérifie:
$ echo 'reset halt' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > reset halt target halted due to debug-request, current mode: Thread xPSR: 00000000 pc: 00000000 msp: 00000000 $ echo 'reset init' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > reset init target halted due to debug-request, current mode: Thread xPSR: 00000000 pc: 00000000 msp: 00000000 $ echo 'mdw 0x40011004' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdw 0x40011004 0x40011004: 44444444
Comme on dit du côté de Valence: Rodriguez ! Autre valeur particulière, page 1087, le DBGMCU_IDCODE à l'adresse 0xE004 2000:
$ echo 'mdw 0xE0042000' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdw 0xE0042000 0xe0042000: 20036410
Il s'agit d'une révision 3 (2003) d'un "medium-density device" (0x410). La taille de la mémoire flash (page 1076) sur 16 bits (half-words):
$ echo 'mdh 0x1FFFF7E0' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdh 0x1FFFF7E0 0x1ffff7e0: 0080
Et bien sur l'identifiant de la carte, page (1077-1078), 2 x 16 bits + 2 * 32 bits:
$ echo 'mdh 0x1FFFF7E8 2' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdh 0x1FFFF7E8 2 0x1ffff7e8: 0048 0024 $ echo 'mdw 0x1FFFF7EC 2' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdw 0x1FFFF7EC 2 0x1ffff7ec: 31333911 41373931
La LED PC13
Pour contrôler la PIN 13 du GPIO C je dois d'abord activer ce dernier. Page 106, dans le "APB2 peripheral reset register", on peut voir que le GPIO C correspond au bit 4. Ce registre se situe à 0x18 de l'adresse de base de RCC (Reset and Clock Control) qui se trouve en 0x4002 1000 (page 50). Je commence par vérifier la valeur initiale puis j'active le GPIO:
$ echo 'mdw 0x40021018' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdw 0x40021018 0x40021018: 00000000 $ echo 'mww 0x40021018 0x00000010' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mww 0x40021018 0x00000010 $ echo 'mdw 0x40021018' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mdw 0x40021018 0x40021018: 00000010
Maintenant que le GPIO est activé, je peux passer la PIN 13 en sortie (Output) depuis le registre de configuration (0x4001 1004, vu plus haut). Pour se faire, les bits 21 et 20 (le "mode") devront avoir pour valeur respective 1 et 0:
$ echo 'mww 0x40011004 0x44644444' | nc -N 127.0.0.1 4444 Open On-Chip Debugger > mww 0x40011004 0x44644444
A ce moment la LED devrait être allumée. Pour changer l'état de la PIN j'utilise les bits 13 et 29 du registre "Port bit set/reset register", page 173, dont le décalage vaut 0x10:
# bit 13 à 1, état haut, LED éteinte $ echo 'mww 0x40011010 8192' | nc -N 127.0.0.1 4444 # bit 29 à 1, état bas, LED allumée $ echo 'mww 0x40011010 536870912' | nc -N 127.0.0.1 4444
Pour conclure
Manipuler les bits et les adresses d'un micro-contrôleur reste pour moi une source d'émerveillement dont je doute me lasser un jour. Il existe un lien naturel entre ce monde et celui de l'informatique "générale" qu'illustre parfaitement cette (célèbre ?) sentence:
Fonctions, variables ? Mensonges ! Tout n'est qu'adresse -- iMil
Il ne me reste plus qu'à lire tranquillement la documentation officielle, la partie concernant Tcl me faisant de l'oeil ...
Commentaires: https://github.com/bsdsx/blog_posts/issues/19
10/06/2023
202306100800 ssh
ssh possède une option assez pratique pour qui veut apporter des modifications à sa configuration sans tout casser:
-G Causes ssh to print its configuration after evaluating Host and Match blocks and exit.
Pour avoir une idée de ce qu'il est possible de configurer:
$ ssh -F /dev/null -G localhost | wc -l 81
On va donc plutôt filtrer la sortie:
$ ssh -F /dev/null -G localhost | grep -e port -e localhost host localhost hostname localhost port 22 gatewayports no nohostauthenticationforlocalhost no
AddressFamily
Cette option permet de définir l'usage d'ipv4, d'ipv6 ou des deux. Comme chez moi, le ssh ne fonctionne qu'en ipv6, je peux ajouter dans mon ~/.ssh/config:
AddressFamily inet6
Sauf que:
- certains pouilleux ne veulent pas faire d'ipv6
- certains équipements sont incompatibles avec ipv6
- on peut vouloir forcer l'usage d'ipv4
Je commence par identifier le problème:
$ echo 'AddressFamily inet6' > /tmp/ssh_config $ ssh -F /tmp/ssh_config github.com ssh: Could not resolve hostname github.com: Address family for hostname not supported $ ssh -F /tmp/ssh_config github.com 192.168.0.1 ssh: Could not resolve hostname 192.168.0.1: Name does not resolve
Je distingue ces citoyens de seconde zone avec une directive Match:
$ cat /tmp/ssh_config Match host github.com,10.*,192.168.* AddressFamily inet Match all AddressFamily inet6
et je teste:
$ ssh -F /tmp/ssh_config -G github.com | grep address addressfamily inet $ ssh -F /tmp/ssh_config -G gitlab.com | grep address addressfamily inet6 $ ssh -F /tmp/ssh_config -G 192.168.0.1 | grep address addressfamily inet
En cas de liste, attention à ne pas utiliser d'espace, uniquement la virgule. Pour le réseau 172.16.0.0/12, c'est un peu plus long:
$ grep 172 /tmp/ssh_config $ Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
Pour mon usage d'ipv4, je ne souhaite pas polluer mon .ssh/known_hosts:
$ cat /tmp/ssh_config Match host github.com,192.168.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6
IdentityFile
J'ai une clef pour chaque serveur publique sur lequel je me connecte et j'ai la bonne idée de la nommer en fonction du serveur:
$ cat /tmp/ssh_config Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 Match host github.com,gitlab.com IdentityFile ~/.ssh/%h
Je teste:
$ ssh -F /tmp/ssh_config -G github.com | grep -e address -e identityfile addressfamily inet identityfile ~/.ssh/%h $ ssh -F /tmp/ssh_config -G gitlab.com | grep -e address -e identityfile addressfamily inet6 identityfile ~/.ssh/%h $ ssh -F /tmp/ssh_config -G localhost | grep -e address -e identityfile addressfamily inet6 identityfile ~/.ssh/id_rsa identityfile ~/.ssh/id_ecdsa identityfile ~/.ssh/id_ecdsa_sk identityfile ~/.ssh/id_ed25519 identityfile ~/.ssh/id_ed25519_sk identityfile ~/.ssh/id_xmss identityfile ~/.ssh/id_dsa
Parce que l'ordre compte
J'utilise toujours une clef sauf pour le tld .ru:
Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 PreferredAuthentications publickey Match host github.com,gitlab.com IdentityFile ~/.ssh/%h Match host *.ru PreferredAuthentications password
Pas marche:
$ ssh -F /tmp/ssh_config -G fail.ru | grep -e preferredauthentications preferredauthentications publickey
On ne peut pas redéfinir une directive, comme indiqué en début de page de manuel:
Unless noted otherwise, for each parameter, the first obtained value will be used.
Pour mon cas d'usage je peux:
- déplacer ma dernière directive avant le Match all
- ajouter en fin de fichier une directive Match all contenant la directive PreferredAuthentications publickey
Include et alias
La directive suivante définit un raccourci vers github.com
Host gh Hostname github.com
que l'on peut placer dans un fichier dédié. Une directive Include :
$ cat /tmp/ssh_config_alias Host gh Hostname github.com $ cat /tmp/ssh_config Include /tmp/ssh_config_alias # Chemin complet car le fichier de config n'est pas un de ceux par défaut Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 Match host github.com,gitlab.com IdentityFile ~/.ssh/%h Match host *.ru PreferredAuthentications password Match all PreferredAuthentications publickey
et le tour est joué:
$ ssh -F /tmp/ssh_config -G gh | grep -e address -e identityfile addressfamily inet identityfile ~/.ssh/%h
J'ai quelques machines virtuelles qui sont dans un sous-domaine dédié:
Host nbsd2?,al3?,fbsd4? Hostname %n.xen
Mais ce n'est pas la bonne solution: le token %n n'est pas disponible dans une directive Hostname. Je vais utiliser les directives Canonicalize :
Match host nbsd2? al3? fbsd4? CanonicalizeHostname yes CanonicalDomains xen.bsdsx.fr # oui, le domaine complet
Si la configuration DNS est correcte alors:
$ ssh -F /tmp/ssh_config -G al30 | grep -e hostname hostname al30.ni3.bsdsx.fr canonicalizehostname true
Control
Quand on doit avoir plusieurs connections vers une machine, on comprend vite l'utilité des directives Control et ça ne coûte pas plus cher de l'activer par défaut:
$ cat /tmp/ssh_config_alias ControlMaster auto ControlPath ~/.ssh/.%r@%h:%p ...
Match vs Host
J'ai toujours utilisé la directive Host, c'est pour ce billet que je suis passé à Match. Ces deux directives ne diffèrent que par leur syntaxe:
Host *.ru *.ch Match host *.ru,*.ch
mais seul Match permet ce genre de chose:
Match host *.prod.mon.domaine user backup localuser !production exec "id -Gn %u | grep -q admin" PreferredAuthentications publickey IdentitiesOnly yes IdentityFile ~/.ssh/id_rsa_backup_with_force_command
Si:
- un utilisateur qui n'est pas 'production' et qui fait partie du groupe 'admin'
- se connecte en tant que 'backup'
- vers une machine du domaine '.prod.mon.domaine'
alors on utilise une clef spécifique. Oui cet exemple est plutôt capillotracté (mais aucun drosophile n'a été ... lors de la rédaction de ce billet).
Un dernier pour la route
C'est plus par principe qu'autre chose mais si il y a bien un truc que je ne supporte pas c'est tout ce qui ne sert à rien (oui, je ne supporte pas grand chose :) . Quand je vois tous les fichiers testés par
$ ssh -v ...
je me dis que ces quelques lignes ne peuvent pas faire de mal:
GlobalKnownHostsFile /dev/null # je n'ai jamais créé les fichiers de cette directive UserKnownHostsFile ~/.ssh/known_hosts # known_hosts2 n'existe pas # Pour ne pas tester des clefs qui n'existent pas IdentityFile ~/.ssh/id_rsa IdentityFile ~/.ssh/id_ecdsa_sk AddKeysToAgent yes # ajouter les clefs à l'agent lors de leur première utilisation
Enjoy !
Commentaires: https://github.com/bsdsx/blog_posts/issues/18
04/03/2023
202303040800 bit perl
Les bits
J'ai toujours eu un peu de mal à avoir la représentation binaire d'une valeur, qu'elle soit au format décimal ou hexadécimal (là j'ai vraiment beaucoup de mal). Mais jouer avec des microcontrôleurs impose tôt ou tard de se retrouver avec ce genre de chose:
RCC->APB1ENR |= 0x200000;
APB1ENR est une valeur sur 32 bits décomposés en bit (parfois en groupe de bit) et ici on active un de ces bits. Mais lequel ? Pour me simplifier la vie, je me suis donc fendu d'un script (Perl, what else ?) sans prétention qui m'affiche, pour chaque argument, sa valeur binaire:
$ perl bits.pl 0x200000 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -------------------------------------------------------------------------------------------------------------------------------- 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x200000 (2097152)
En consultant la documentation, je comprend que le bit 21 du registre APB1ENR correspond (pour une blue pill) à l'activation de I2C1. Parce que certains registres sont sur 16 bits, je peux aussi réduire le nombre de bit à afficher:
$ perl bits.pl -16 0x800 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ---------------------------------------------------------------- 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0x800 (2048)
ou modifier la largeur de chaque colonne (pas le plus simple à lire, j'en conviens):
$ perl bits.pl -8 -w 1 5 7 9 76543210 -------- 00000101 5 (5) 00000111 7 (7) 00001001 9 (9)
Je peux aussi utiliser des valeurs binaires directement à des fins de comparaison:
$ perl bits.pl -8 0b00000111 0b10101010 5 0xa 7 6 5 4 3 2 1 0 -------------------------------- 0 0 0 0 0 1 1 1 0b00000111 (7) 1 0 1 0 1 0 1 0 0b10101010 (170) 0 0 0 0 0 1 0 1 5 (5) 0 0 0 0 1 0 1 0 0xa (10)
Les opérateurs et fonctions Perl
Avec l'opérateur .. je peux facilement obtenir la liste des nombres de 0 à 15:
$ perl -le 'print 0 .. 15' 0123456789101112131415
Mais pas ceux de 15 à 0:
$ perl -le 'print 15 .. 0' <rien, nada, que dalle>
Pour afficher mes nombres dans l'ordre décroissant, il me suffit d'utiliser reverse:
$ perl -le 'print reverse 0 .. 15' 1514131211109876543210
Pour afficher une valeur sur 3 caractères j'utilise %3s. Pour formater size bits sur une largeur de width il me suffit d'utiliser l'opérateur x:
$fmt = "%${width}s" x $size-- . '%s'; // on n'oublie pas le '%s' pour le retour à la ligne
Parce que j'utilise reverse, je dois commencer par le retour chariot:
printf $fmt, reverse "\n", 0 .. $size;
Pour connaitre la valeur d'un bit à la position $pos d'une valeur $value, je dois déplacer la valeur 1 de $pos positions:
1 << $pos
faire un et avec la valeur:
$value & (1 << $pos)
et redécaler ce résultat de pos positions (les parenthèses sont nécessaires du fait de la priorité des opérateurs):
($value & (1 << $pos)) >> $pos
Pour obtenir la valeur des $size premiers bits de $register:
map { ($register & (1 << $_)) >> $_ } 0 .. $size;
map va appliquer le block ({ .. }) pour chaque valeur ($_) de la list 0 .. $size
Le script
Comme d'habitude, pour quelques lignes de code, on se trouve avec une gestion des options, de l'aide, de la vérification d'arguments ... et paf, 50 lignes:
$ cat -n bits.pl 1 #!/usr/bin/perl 2 3 use strict; 4 use warnings FATAL => 'all'; 5 use utf8; 6 7 use Getopt::Long; 8 9 sub usage { 10 print <<EOT 11 Usage: $0 [-8|-16|-32] [--width=width] [-h] number ... 12 13 Example: 14 $0 42 0xcafe 0b101 15 $0 -8 -w 1 5 16 EOT 17 ; 18 exit; 19 } 20 21 my ($size, $width, $help) = (32, 4); 22 GetOptions( 23 '8' => sub { $size = 8 }, 24 '16' => sub { $size = 16 }, 25 '32' => sub { $size = 32 }, 26 27 'width=i' => \$width, 28 'help' => \$help, 29 ) or usage(); 30 31 if ($help || !scalar @ARGV) { usage(); } 32 33 my $fmt; 34 sub dump_register { 35 my $register = $_[0] =~ /^0[bx]/i ? oct $_[0] : $_[0]; 36 printf $fmt, reverse " $_[0] ($register)\n", map { ($register & (1 << $_)) >> $_ } 0 .. $size; 37 } 38 39 if ($width < 2 && $size > 8) { $width = 3; } 40 $fmt = "%${width}s" x $size-- . '%s'; 41 42 printf $fmt, reverse "\n", 0 .. $size; 43 printf $fmt, (('-' x $width)) x ($size + 1) , "\n"; 44 45 foreach (@ARGV) { dump_register($_); }
Pour finir
Quand je tombe sur ce genre de chose:
GPIOA->CRL &= ~(0xf << (2 * 4)); GPIOA->CRL |= 0xA << (2 * 4);
Il me suffit de rajouter en fin de script:
my $register = $ARGV[0] =~ /^0[bx]/i ? oct $ARGV[0] : $ARGV[0]; $register &= ~(0xf << (2 * 4)); dump_register($register); $register |= 0xA << (2 * 4); dump_register($register);
et de le lancer avec 0xffffffff (pour que tous les bits soient à 1):
$ perl bits.pl 0xffffffff 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -------------------------------------------------------------------------------------------------------------------------------- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xffffffff (4294967295) 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 4294963455 (4294963455) 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 4294966015 (4294966015)
La première instruction initialise à zéro le 3ème groupe de 4 bits, la seconde active les bits 1 (9) et 3 (11) de ce 3ème groupe. Et tout est plus clair !
Commentaires: https://github.com/bsdsx/blog_posts/issues/17