Créer un bouton sans fil sous esp8266 (01/01s) pour démarrer son PC (magic packet)

#main.py Projet à télécharger ici https://monkeytime.be/blog/data/medias/powa.zip Je l'ai créer avec ce type de bouton acheté sur Amazon en lui adaptant un petit boitier pour contenir l'électronique et ce petit bouton power.

try:
  import usocket as socket
except:
  import socket
import network
from time import sleep
import ubinascii
from micropython import const
HEADER = "F" * 12

LED = machine.Pin(0, machine.Pin.OUT)
LED.value(0)

def create_magic_packet(macaddress: str) -> bytes:

    if len(macaddress) == 17:
        sep = macaddress[2]
        macaddress = macaddress.replace(sep, "")
    elif len(macaddress) != 12:
        raise ValueError("Incorrect MAC address format")

    return ubinascii.unhexlify(HEADER + macaddress * 16)


def send_magic_packet(
    *macs: str,
    ip_address: str,
    port: int = 9,
    interface: str = None
) -> None:  

    packets = [create_magic_packet(mac) for mac in macs]
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    if interface is not None:
        sock.bind((interface, 0))
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.connect((ip_address, port))
    for packet in packets: 
        sock.send(packet) 
    sock.close()


_hexdig = '0123456789ABCDEFabcdef'
_hextobyte = None
def unquote(string):
    """unquote('abc%20def') -> b'abc def'."""
    global _hextobyte

    # Note: strings are encoded as UTF-8. This is only an issue if it contains
    # unescaped non-ASCII characters, which URIs should not.
    if not string:
        return b''

    if isinstance(string, str):
        string = string.encode('utf-8')

    bits = string.split(b'%')
    if len(bits) == 1:
        return string

    res = [bits[0]]
    append = res.append

    # Delay the initialization of the table to not waste memory
    # if the function is never called
    if _hextobyte is None:
        _hextobyte = {(a + b).encode(): bytes([int(a + b, 16)])
                      for a in _hexdig for b in _hexdig}

    for item in bits[1:]:
        try:
            append(_hextobyte[item[:2]])
            append(item[2:])
        except KeyError:
            append(b'%')
            append(item)

    return b''.join(res)

def start_access_point():
    
    global ap
    
    ssid = 'POWA-AP'
    password = '123454321'

    ap = network.WLAN(network.AP_IF)
    ap.active(True)
    ap.config(essid=ssid, password=password)

    while ap.active() == False:
      pass

    print('AP start successful')
    print(ap.ifconfig())

def stop_access_point():
    if ('ap' in globals()):
        ap.active(False)
    
def stop_socket_loop():
    if ('conn' in globals()):
        conn.close()
    
    
def parse_params(part):
    parameters = {}
    for piece in part.split(" "):
        if "/?" in piece:
            piece = piece.replace("/?", "")
            amp_split = piece.split("&")
            for param_set in amp_split:
                eq_split = param_set.split("=")
                parameters[eq_split[0]] = eq_split[1]
    return parameters

def send_config_page():
    f = open('index.html', 'r')
    while True:
        chunk = f.read(5024)
        if not chunk:
            break
        conn.sendall(str(chunk))
    conn.close()
    f.close()
    
def web_page_success():
    html = """<html><body><h1 style="color:green; position: absolute; top: 50%; left: 45%; width:10%">SUCCESS</h1></body></html>"""
    return html

def do_connect():
    global sta_if
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        try:
            fssid = open('ssid.dat')
            fpass = open('password.dat')
        except OSError:
            print('Open files error')
            fssid = ""
            fpass = ""
        sta_if = network.WLAN(network.STA_IF)
        sta_if.active(True)             
        sta_if.connect(fssid.readline(), fpass.readline())
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())
    a = sta_if.config('mac')
    print('MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(a[0],a[1],a[2],a[3],a[4]))

def boot():
    try:
        fmac = open('mac.dat')
        mac = fmac.readline()
        mask = sta_if.ifconfig()[1].split('.')[3]
        if(mask == '255'):
            mask = '0'
        else:
            mask = '255'
        ip = sta_if.ifconfig()[2].split('.')
        ip_broadcast = ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + mask
        print('Broadcast ip is ' + ip_broadcast)
    except OSError:
        print('Open files error')
    send_magic_packet(str(mac), ip_address = str(ip_broadcast))
    sleep(0.5)
    print('Magic packet sent for ' + mac)

def stop_wifi():
    if('sta_if' in globals):
        sta_if.active(False)

def start_socket_loop():
    gc.collect()
    addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setblocking(False)
    s.bind((addr))
    s.listen()
    s.settimeout(None)
    
    while True:
      global conn
      conn, addr = s.accept()
      print('Got a connection from %s' % str(addr))
      request = conn.recv(4096)
      print('Content = %s' % str(request))
      params = parse_params(str(request))
      
      if(params.get('mac') is not None and params.get('ssid') is not None and params.get('password') is not None):
          #get ssid/password Home wifi drom the android browser
          sta_if = network.WLAN(network.STA_IF)
          sta_if.active(True)             
          sta_if.connect(params.get('ssid'), params.get('password'))
          while not sta_if.isconnected():
              pass
            
          if(sta_if.isconnected()):

              f = open('mac.dat', 'w')
              f.write(unquote(str(params.get('mac'))))
              f.close()
              
              f = open('ssid.dat', 'w')
              f.write(unquote(str(params.get('ssid'))))
              f.close()
              
              f = open('password.dat', 'w')
              f.write(unquote(str(params.get('password'))))
              f.close()
              
              response = web_page_success()
              conn.send('HTTP/1.1 200 OK\n')
              conn.send('Content-Type: text/html\n')
              conn.send('Connection: close\n\n')
              conn.sendall(str(response))
              break
          else:
              send_config_page()
      else:
          send_config_page()
      gc.collect()
      sleep(0.5)

    gc.collect()
    sleep(0.5)


#app
do_connect()

if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('woke from a deep sleep')
    if(sta_if.isconnected()):
        print('Connected to home wifi')
        boot()
        stop_socket_loop()
        stop_access_point()
        print('Turn to deep sleep')
        machine.deepsleep()
    else:
        start_access_point()
        start_socket_loop()
else:
    print('power on or hard reset')
    if(sta_if.isconnected()):
        print('Connected to home wifi')
        boot()
        stop_socket_loop()
        stop_access_point()
        print('Turn to deep sleep')
        machine.deepsleep()
    else:
        start_access_point()
        start_socket_loop()

# Idée pour l'avenir: arrondir les achats par carte bancaire à l'unité supérieure pour créer une cagnotte pour les enfants

L'idée d'arrondir les achats par carte bancaire à l'unité supérieure pour créer une cagnotte pour les enfants est intéressante et pourrait offrir plusieurs avantages. Tout d'abord, cette approche simplifie considérablement le processus d'épargne pour les parents. Au lieu de devoir planifier et effectuer des transactions distinctes vers une cagnotte pour chaque enfant, le montant est automatiquement arrondi à chaque achat, créant ainsi une cagnotte pour chacun d'eux de manière transparente et sans effort supplémentaire de la part des parents. En outre, cette méthode favorise l'épargne à long terme pour les enfants. Les petites sommes d'argent accumulées à partir des arrondis peuvent représenter une somme significative sur la durée, créant ainsi une cagnotte qui pourrait être utilisée pour financer des études, des voyages ou d'autres projets importants dans la vie de l'enfant. Sur le plan psychologique, cette approche pourrait également sensibiliser les enfants à la valeur de l'argent et à l'importance de l'épargne. En voyant leur cagnotte grandir au fil du temps, les enfants peuvent développer des habitudes financières saines et comprendre les bénéfices de l'épargne à un âge précoce. Côté technique, en tant que web développeur, vous pourriez mettre en place une plateforme en ligne sécurisée qui gère ces transactions automatiques d'arrondissement et qui permet aux parents de suivre et de gérer les cagnottes individuelles de leurs enfants. Cette plateforme pourrait également inclure des fonctionnalités pour définir des objectifs spécifiques d'épargne et pour retirer des fonds de manière sécurisée lorsque nécessaire. En conclusion, cette idée d'arrondir les achats pour créer des cagnottes pour les enfants combine simplicité, efficacité et éducation financière. Elle offre aux parents un moyen pratique d'épargner pour l'avenir de leurs enfants tout en inculquant de saines habitudes financières dès le plus jeune âge.

idée pour le climat #2: Grappes de turbines réduites en taille à placer le long des berges fluviales des grandes villes et métropoles à connecter au réseau électrique

1. Introduction: L'idée d'installer des petites turbines électriques fluviales le long des berges pour alimenter les lampes de voirie représente une solution novatrice et durable pour répondre aux besoins énergétiques des zones riveraines. En exploitant l'énergie cinétique de l'eau en mouvement, cette approche offre une source d'énergie renouvelable et propre, tout en présentant l'avantage supplémentaire de pouvoir injecter l'électricité produite dans le réseau électrique traditionnel. 2. Fonctionnement du système: La turbine électrique fluviale serait installée le long des berges, permettant à l'eau de la rivière de faire tourner les pales de la turbine. Cette rotation générerait de l'électricité qui, au lieu d'être uniquement stockée, pourrait être intégrée au réseau électrique existant. Ainsi, cette électricité pourrait être utilisée non seulement pour alimenter les lampes de voirie locales mais également pour contribuer à l'approvisionnement énergétique global. 3. Avantages environnementaux: L'un des principaux avantages de cette solution réside dans son impact environnemental réduit. En utilisant une source d'énergie renouvelable, le système contribue à la réduction des émissions de gaz à effet de serre et diminue la dépendance aux combustibles fossiles. L'intégration au réseau électrique traditionnel permet également de partager cette énergie propre avec d'autres parties de la région. 4. Autonomie énergétique et contribution au réseau: La mise en œuvre de ce système permettrait aux berges d'atteindre une certaine autonomie énergétique tout en contribuant activement à la stabilité du réseau électrique global. En utilisant l'énergie locale disponible, les communautés riveraines pourraient non seulement répondre à leurs besoins locaux mais aussi partager l'excédent d'électricité générée avec d'autres zones, renforçant ainsi la résilience de l'ensemble du réseau. 5. Impact social et économique: Cette solution offre des avantages sociaux en améliorant la sécurité et la visibilité le long des voies navigables, tout en créant de nouvelles opportunités économiques pour les communautés riveraines. L'intégration au réseau électrique traditionnel renforce également la viabilité économique du projet, attirant l'attention des parties prenantes locales et internationales, et favorisant ainsi l'adoption de technologies durables dans la région.

idée pour le climat #1: un smartphone à puce intel avec thunderbolt que l'on peut "docker" à des appareils complètement passifs

L'idée d'un smartphone avec une puce Intel permettant une modularité accrue à travers des docks spécialisés intégrés à des appareils passifs offre plusieurs avantages significatifs. Tout d'abord, cette approche favorise la durabilité et la réduction de la consommation des ressources naturelles, puisque les composants principaux du smartphone sont contenus dans une unité centrale, réduisant ainsi la nécessité de produire des dispositifs complets pour chaque besoin spécifique. En optant pour un système modulaire, l'utilisateur peut transformer son smartphone en une multitude de dispositifs, tels qu'un ordinateur portable, une station d'accueil pour une carte graphique de bureau, ou même un système de divertissement pour la télévision. Cette polyvalence apporte une flexibilité exceptionnelle, car l'utilisateur n'est plus limité à une seule plateforme ou configuration matérielle. Un avantage écologique majeur réside dans la réduction du gaspillage électronique. Plutôt que de remplacer entièrement un appareil lorsqu'une mise à niveau est nécessaire, les utilisateurs peuvent simplement mettre à niveau la puce centrale de leur smartphone, réduisant ainsi l'impact environnemental de la production de nouveaux dispositifs. Par ailleurs, ce concept offre une solution pratique pour les familles partageant un même espace de vie. Le dock intégré à la télévision permet à un membre de la famille de transformer rapidement son smartphone en un centre de divertissement, tout en continuant à recevoir des appels via une oreillette Bluetooth munie d'un petit écran. Cela élimine la nécessité d'avoir plusieurs dispositifs électroniques distincts, rationalisant ainsi l'espace et réduisant la surconsommation. En résumé, un smartphone modulaire avec une puce Intel et des docks spécialisés représente une avancée significative vers une consommation plus responsable, une réduction du gaspillage électronique, et une meilleure utilisation des ressources naturelles. En offrant aux utilisateurs une flexibilité inédite, cette approche répond aux besoins diversifiés de manière durable et pratique, tout en ouvrant la voie à une nouvelle ère de technologie plus respectueuse de l'environnement.

ChatGPT et correction de la programmation

le code injecté dans chatGPT


import uos

try:
  import usocket as socket
except:
  import socket

import network
import time

import esp
esp.osdebug(None)

import ujson

import gc
gc.collect()

ap = network.WLAN(network.AP_IF)

def verify_user_WiFi():
    if uos.stat("wifi.json")[6] == 0:
        return False
    with open('wifi.json', 'w') as json_file:
        data = ujson.load(json_file)
        if data["ssid"] is None or data["key"] is None:
            return False
        sta_if = network.WLAN(network.STA_IF)
        if not sta_if.isconnected():
            print('Testing connection to your wifi network...')
            sta_if.active(True)
            sta_if.connect(data["ssid"], data["key"])
            if not sta_if.isconnected():
                return False
    

def start_AP():
    ssid = 'BULB-AP'
    password = '123456789'

    ap.active(True)
    ap.config(essid=ssid, password=password)

    while ap.active() == False:
      pass

    print('Starting AP successful')
    print(ap.ifconfig())

def stop_AP():
    ap.active(False)
    print('Stopping AP successful')
    
def do_connect_to_user_WiFi(ssid, key):
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect(ssid, key)
        if not sta_if.isconnected():
            return False
    print('network config:', sta_if.ifconfig())
    
def listen():
    start_AP()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 80))
    s.listen(5)

    while True:
      conn, addr = s.accept()
      print('Got a connection from %s' % str(addr))
      request = conn.recv(4096).decode('utf-8')
      print('Content = %s' % str(request))
      data = request.json()
      if data["ssid"] is not None and data["key"] is not None:
          if do_connect_to_user_WiFi(data["ssid"], data["key"]) == False:
              conn.send("invalid")
              conn.close()
          conn.send("connected")
          with open('wifi.json', 'w') as json_file:
              dictionary = {"ssid": data["ssid"], "key": data["key"]}
              ujson.dump(dictionnary, json_file)
          conn.close()
          stop_AP()
      conn.close()
      
if verify_user_WiFi() == False:
    listen()

Le texte "commentaire" sorti par chatGPT

This program sets up an access point (AP) on the ESP8266 with the name "BULB-AP" and password "123456789". It then opens a socket on port 80 and listens for incoming connections. When a client connects, it reads the request and checks if it contains valid wifi credentials in the form of a json object with "ssid" and "key" fields. If the wifi credentials are valid, the ESP8266 connects to the specified wifi network and saves the credentials to a file named "wifi.json" and sends "connected" to the client. If the wifi credentials are not valid, it sends "invalid" to the client and continues to listen for connections. It also has a function named verify_user_WiFi() which check if the wifi.json file exist and contain valid ssid and key, if not, it starts the AP. It is important to note that this code does not include error handling for edge cases and invalid input, such as a file not being found or improperly formatted JSON data. In a real-world implementation, you would want to include additional error handling and testing to ensure that the program is robust and can handle a wide range of inputs.

La correction en injectant le texte

Create a program that sets up an access point (AP) on the ESP8266 with the name "BULB-AP" and password "123456789". It then opens a socket on port 80 and listens for incoming connections. When a client connects, it reads the request and checks if it contains valid wifi credentials in the form of a json object with "ssid" and "key" fields. If the wifi credentials are valid, the ESP8266 connects to the specified wifi network and saves the credentials to a file named "wifi.json" and sends "connected" to the client. If the wifi credentials are not valid, it sends "invalid" to the client and continues to listen for connections. It also has a function named verify_user_WiFi() which check if the wifi.json file exist and contain valid ssid and key, if not, it starts the AP.


import network
import socket
import ujson

def verify_user_WiFi():
    try:
        with open('wifi.json', 'r') as json_file:
            data = ujson.load(json_file)
            if "ssid" not in data or "key" not in data:
                raise ValueError("Invalid wifi json file")
            sta_if = network.WLAN(network.STA_IF)
            if not sta_if.isconnected():
                print('Testing connection to user wifi network...')
                sta_if.active(True)
                sta_if.connect(data["ssid"], data["key"])
                if sta_if.isconnected():
                    print("Connected to wifi network")
                    return True
                else:
                    raise ValueError("Cannot connect to wifi network")
    except (OSError, ValueError):
        print("WiFi credentials not found, starting AP")
        return False

def start_AP():
    ssid = 'BULB-AP'
    password = '123456789'

    ap = network.WLAN(network.AP_IF)
    ap.active(True)
    ap.config(essid=ssid, password=password)

    while ap.active() == False:
      pass

    print('Access Point started successfully')
    print(ap.ifconfig())

def stop_AP():
    ap = network.WLAN(network.AP_IF)
    ap.active(False)
    print('Access Point stopped successfully')

def do_connect_to_user_WiFi(ssid, key):
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('Connecting to network...')
        sta_if.active(True)
        sta_if.connect(ssid, key)
        if not sta_if.isconnected():
            return False
    print('Connected to wifi network:', sta_if.ifconfig())
    return True

def listen():
    start_AP()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 80))
    s.listen(5)

    while True:
        conn, addr = s.accept()
        print('Got a connection from %s' % str(addr))
        request = conn.recv(4096).decode('utf-8')
        print('Content = %s' % str(request))
        try:
            data = ujson.loads(request)
            if "ssid" not in data or "key" not in data:
                raise ValueError("Invalid wifi json")
            if do_connect_to_user_WiFi(data["ssid"], data["key"]) == False:
                conn.send("invalid".encode())
                conn.close()
                continue
            with open('wifi.json', 'w') as json_file:
                ujson.dump(data, json_file)
            conn.send("connected".encode())
            stop_AP()
        except (ValueError,OSError) as e:
            print(e)

PWA (Progressive Web App) init, un exemple fonctionnel

Si vous voulez démarrer avec un site web qui peut s'installer comme application sur un smartphone (c'est une option du navigateur qui s'affiche et qui proposera l'installation), voici un petit dossier d'exemple, un petit "Hello World", pour démarrer votre projet


<!-- le code du fichier index.html -->
<!DOCTYPE html>
<html lang="fr" dir="ltr">

    <head>

    <meta name="theme-color" content="#ffffff">

    <script src="/js/https.js" type="text/javascript" async></script>

    <meta charset="utf-8">
    <link rel="manifest" crossorigin="use-credentials" href="/manifest.json">

    <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><![endif]-->
    <meta name="viewport" content="initial-scale=1.0">

    <title>Your site title</title>

    <meta content="follow,index" name="robots">
    <meta name="description" content="Your site description">
    <meta name="keywords" content="some, thing">

    <meta name="author" content="First Name Last Name">
    <meta name="copyright" content="All rights reserved">

    <link rel="icon" type="image/png" href="/favicon.png" />
    <link rel="icon" type="image/vnd.microsoft.icon" href="/favicon.ico">
    <!--[if IE]><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /><![endif]-->

    <meta property="og:type" content="website">
    <meta property="og:locale" content="en">
    <meta property="og:site_name" content="Site Name">
    <meta property="og:title" content="Page Title">
    <meta property="og:description" content="Page description">

    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="mobile-web-app-capable" content="yes">
    <link rel="apple-touch-icon" title="Site title" sizes="57x57" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="114x114" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="72x72" href="/touch-retina.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="144x144" href="/touch-retina.webp">

    <link rel="apple-touch-icon" title="Site title" sizes="57x57" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="60x60" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="72x72" href="/touch-retina.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="76x76" href="/touch-retina.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="114x114" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="120x120" href="/touch-icon.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="144x144" href="/touch-retina.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="152x152" href="/touch-retina.webp">
    <link rel="apple-touch-icon" title="Site title" sizes="180x180" href="/touch-retina.webp">
    <link rel="icon" type="image/png" title="Site title" href="/favicon.webp" sizes="32x32">
    <link rel="icon" type="image/png" title="Site title" href="/touch-retina.webp" sizes="192x192">
    <link rel="icon" type="image/png" title="Site title" href="/touch-retina.webp" sizes="96x96">
    <link rel="icon" type="image/png" title="Site title" href="/favicon.webp" sizes="16x16">
    <link rel="icon" type="image/png" title="Site title" href="/favicon.webp" />

    <meta name="msapplication-TileColor" content="transparent">
    <meta name="msapplication-TileImage" content="/touch-retina.webp">

    <!--[if lt IE 9]><script src="/js/html5shiv.js" type="text/javascript"></script><script src="/js/respond.js" type="text/javascript"></script><![endif]-->

    </head>

     <body>

        <h1>Hello World !</h1>

    </body>

</html>


<link rel="stylesheet" type="text/css" href="/css/main.css" media="all">

<script type="text/javascript" async>
window.onload = () => {
	'use strict';
  
	if ('serviceWorker' in navigator) {
	  navigator.serviceWorker
			   .register('./sw.js');
	}
}
</script>

https://github.com/MonkeyTime/PWA-init

Les éditeurs ou IDE indispensables

1 Atom https://atom.io + package Pymakr

est exempt de bug pour le développement micropython ou sous Pycom

 

2 Thonny https://thonny.org

idéal si vous débutez le développement ESP32 ou ESP8266

 

3 Studio Code https://code.visualstudio.com

pour tous autres langages de programmation (pour ma part surtout javascript) ou

pour tous frameworks type electronjs, Cordova, nodejs, Meteorjs (mais l'extension Pymakr pour Visual Studio Code a un bug)

 

4 Eclipse PHP IDE https://www.eclipse.org/pdt

j'aime bien pour gérer et développer mes projets PHP "from scratch" et sans auto-complétion.

TTGO oled ESP32 et SSD1306

Une librairie SSD1306 pour TTGO oled ESP32 qui fonctionne.


from machine import SoftI2C, Pin
import ssd1306

# ESP32 Pin assignment
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))

# ESP8266 Pin assignment
#i2c = I2C(-1, scl=Pin(5), sda=Pin(4))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
oled.fill(0)
oled.text('Hello, World 1!', 0, 0)
oled.text('Hello, World 2!', 0, 10)
oled.text('Hello, World 3!', 0, 20)
oled.show()

# # If you need to disable oled in production environment
# from machine import SoftI2C, Pin
# import ssd1306
# 
# # ESP32 Pin assignment
# i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
# 
# # ESP8266 Pin assignment
# #i2c = I2C(-1, scl=Pin(5), sda=Pin(4))
# 
# oled_width = 128
# oled_height = 64
# oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# oled.fill(0)
# oled.poweroff()

https://github.com/MonkeyTime/TTGO-Oled-ESP32-SSD1306-library