diff --git a/.ropeproject/config.py b/.ropeproject/config.py new file mode 100644 index 0000000..dee2d1a --- /dev/null +++ b/.ropeproject/config.py @@ -0,0 +1,114 @@ +# The default ``config.py`` +# flake8: noqa + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git', '.tox'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + # prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + # prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + # prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + # If `True`, rope will insert new module imports as + # `from import ` by default. + prefs['prefer_module_from_imports'] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs['split_imports'] = False + + # If `True`, rope will remove all top-level import statements and + # reinsert them at the top of the module when making changes. + prefs['pull_imports_to_top'] = True + + # If `True`, rope will sort imports alphabetically by module name instead + # of alphabetically by import statement, with from imports after normal + # imports. + prefs['sort_imports_alphabetically'] = False + + # Location of implementation of + # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general + # case, you don't have to change this value, unless you're an rope expert. + # Change this value to inject you own implementations of interfaces + # listed in module rope.base.oi.type_hinting.providers.interfaces + # For example, you can add you own providers for Django Models, or disable + # the search type-hinting in a class hierarchy, etc. + prefs['type_hinting_factory'] = ( + 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/code/.gitkeep b/code/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/code/boot.py b/code/boot.py index 0ce4ca6..27b198c 100644 --- a/code/boot.py +++ b/code/boot.py @@ -1,7 +1,6 @@ # This file is executed on every boot (including wake-boot from deepsleep) #import esp #esp.osdebug(None) - import gc import time import network @@ -9,22 +8,29 @@ import webrepl from wifi_config import known_wifi_ap # Connect to one of the known wifi AP -interface = network.WLAN(network.STA_IF) -interface.active(True) -wifi_ap_list = interface.scan() +WLAN = network.WLAN(network.STA_IF) +WLAN.active(True) +wifi_ap_list = WLAN.scan() for ap in wifi_ap_list: - if interface.isconnected(): + if WLAN.isconnected(): break ap_ssid = ap[0].decode("utf-8") if ap_ssid in known_wifi_ap.keys(): print("Known wifi network found : {}".format(ap_ssid)) print("Try to connect...") - interface.connect(ap_ssid, known_wifi_ap[ap_ssid]) + WLAN.connect(ap_ssid, known_wifi_ap[ap_ssid]) -# Wait for wifi -time.sleep(5) + DELAY = 0 + + while not WLAN.isconnected(): + print("Waiting for wifi to connect...") + time.sleep(1) + DELAY += 1 + if DELAY > 10: + print("Wifi time-out") + break webrepl.start() diff --git a/code/encoder.py b/code/lib/encoder.py similarity index 100% rename from code/encoder.py rename to code/lib/encoder.py diff --git a/code/lib/light_modes.py b/code/lib/light_modes.py new file mode 100644 index 0000000..da59d6b --- /dev/null +++ b/code/lib/light_modes.py @@ -0,0 +1,77 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Modes for neopixels strip +Each function update the NeoPixel strip buffer (except update_neopixel() that +call them) +""" +__author__ = "arofarn" +__version__ = 0.1 + +import time +import urandom + + +def update_neopixel(mode, np_strp, col=(255, 255, 255), bri=50): + """Update NeoPixel strip with one color + :param np_strp : NeoPixel object + :param mode : mode ;) + :param col : color [R, G, B] (default: [255, 255, 255]) + :param bri : brightness 0-100 (default: 50) + """ + if bri > 100: + bri = 100 + elif bri < 0: + bri = 0 + bri_coef = bri/100 + # Apply mode + MODES_LST[mode](np_strp, col) + # Apply brightness to the whole LEDs + np_strp.buf = bytearray([int(x * bri_coef) for x in np_strp.buf]) + # Finally display color on LEDs + np_strp.write() + + +def fill_usr(np_strp, col): + """Update NeoPixel strip with one color + :param np_strp : NeoPixel object + :param col : color [R, G, B] + """ + np_strp.fill(col) + + +def fill_white(np_strp, col=(255, 255, 255)): + """Update NeoPixel strip with only white + :param np_strp : NeoPixel object + :param col : color [R, G, B] (ignored, default=(255, 255, 255)) + """ + np_strp.fill((255, 255, 255)) + + +def sparkles(np_strp, col): + """Make Neopixel sparkle with user defined color!!!""" + np_strp.fill((0, 0, 0)) + + for _ in range(int(np_strp.n / 4)): + pix = int(urandom.getrandbits(8) / 256 * (np_strp.n)) + np_strp[pix] = col + + +def christmas(np_strp, col): + """Shine like christmas tree °<:oD + TODO !!! + """ + pass + + +MODES_LST = (fill_usr, fill_white, sparkles) diff --git a/code/main.py b/code/main.py index e20dd3b..12226f1 100644 --- a/code/main.py +++ b/code/main.py @@ -16,92 +16,129 @@ Eclairage à LED Neopixel avec contrôle (encodeur rotatif) """ __author__ = "arofarn" -__version__ = 0.2 +__version__ = 0.3 # Imports import time +import sys import machine import neopixel - -from uos import uname from encoder import Encoder -#Paramètres +import light_modes -NB_PIX = 8 # Nombre de pixels -MAX_BRIGHT = 100 # Luminosité max (100 max.) -COLOR = [255, 255, 255] # Couleur de base (à luminosité max) +# Paramètres -#Déclaration des objets et initialisation des variables +NB_PIX = 67 # Nombre de pixels de la 1e bande de LED +NB_PIX2 = 67 # Nombre de pixels de la 2e bande de LED +MAX_BRIGHT = 100 # Luminosité max (100 max.) +USR_COLOR = [255, 130, 20] # Couleur de base (à luminosité max) -BRD_TYPE = uname()[0] -print("Système :", BRD_TYPE) +# Déclaration des objets et initialisation des variables -if BRD_TYPE == 'esp8266': - NEOPIX_PIN = 2 - # Les deux broches suivantes doivent être capable d'interruption !!! - # sur ESP8266 => : 4, 5, 12, 13 et 14 - ENC_PIN_A = 13 # N° de la 1ere broche de l'encodeur - ENC_PIN_B = 12 # N° de la 2e broche de l'encodeur - ENC_PIN_C = 14 # broche du clic central de l'encodeur +NEOPIX_PIN = 2 +NEOPIX2_PIN = 16 +# Les deux broches suivantes doivent être capable d'interruption !!! +# sur ESP8266 => : 4, 5, 12, 13 et 14 +ENC_PIN_A = 14 # N° de la 1ere broche de l'encodeur +ENC_PIN_B = 12 # N° de la 2e broche de l'encodeur +ENC_PIN_C = 13 # broche du clic central de l'encodeur - # With ESP8266 (no timing parameter) - NPXL_STRIP = neopixel.NeoPixel(machine.Pin(NEOPIX_PIN), NB_PIX) +WIFI_LED_PIN = 0 -elif BRD_TYPE == 'esp32': - NEOPIX_PIN = 14 - # Pins - ENC_PIN_A = 15 # N° de la 1ere broche de l'encodeur - ENC_PIN_B = 33 # N° de la 2e broche de l'encodeur - ENC_PIN_C = 27 # broche du clic central de l'encodeur +# With ESP8266 (no timing parameter) +NPXL_STRIP = neopixel.NeoPixel(machine.Pin(NEOPIX_PIN), NB_PIX) +NPXL_STRIP2 = neopixel.NeoPixel(machine.Pin(NEOPIX2_PIN), NB_PIX) - # # Only with ESP32 : add timing param. - # # timing param =0 for "old" 400kHz neopixel (default) - # # =1 for "new" 800kHz neopixel - NPXL_STRIP = neopixel.NeoPixel(machine.Pin(NEOPIX_PIN), NB_PIX, timing=1) - -else: - # ce code n'est pas prévu pour d'autre carte pour l'instant - print("Carte non-supportée :", BRD_TYPE) - quit() - -# # Only with ESP32 : add timing param. -# # timing param =0 for "old" 400kHz neopixel (default) -# # =1 for "new" 800kHz neopixel -# pixel_strip = neopixel.NeoPixel(machine.Pin(NEOPIX_PIN), NB_PIX, timing=1) -# coul_tmp = [0, 0, 0] - -#Eteint tout à l'initialisation +# Eteint tout à l'initialisation NPXL_STRIP.fill([0, 0, 0]) NPXL_STRIP.write() # Encodeur rotatif -ENCODER = Encoder(ENC_PIN_B, ENC_PIN_A, +ENCODER = Encoder(ENC_PIN_A, ENC_PIN_B, min_val=0, max_val=MAX_BRIGHT, clicks=1) -#Bouton +# Bouton ENC_BUT = machine.Pin(ENC_PIN_C, machine.Pin.IN) -#Variables d'état +# LED status WIFI_LED_PIN +WIFI_LED = machine.Pin(WIFI_LED_PIN, machine.Pin.OUT) + +# initialisation des variables d'état BRIGHTN = 50 # Luminosité (0 - 100) -PWR = False # Est-ce que l'éclairage est allumé ? +PWR = True # Est-ce que l'éclairage est allumé ? BUTTN_STATE = 1 +CURRENT_MODE = 0 +PWROFF_DELAY = 0 -def update_neopixel(np_strp, col, bri=50): - """Update NeoPixel strip with one color - :param np_strp : NeoPixel object - :param col : color [R, G, B] - :param bri : brightness 0-100 (default: 50) - """ - col_tmp = [0, 0, 0] +############# +# FUNCTIONS # +############# - for i in range(3): - col_tmp[i] = int(col[i] * bri / 100) - np_strp.fill(col_tmp) - np_strp.write() +def power_cycle(): + """ON/OFF function""" + global PWR + global BRIGHTN + + PWR = not PWR + print("Power :", PWR) + if not PWR: + # Extinction des LED + NPXL_STRIP.fill((0, 0, 0)) + NPXL_STRIP.write() + else: + # Luminosité basse si on rallume avec une luminisité de 0 + if BRIGHTN == 0: + BRIGHTN = 10 + + # On remet l'encodeur à la dernière luminosité connue + # pour ignorer les mouvements pendant l'extinction + ENCODER.reset(BRIGHTN) + + # on attend que le bouton soit relâché + while ENC_BUT.value() == 0: + time.sleep_ms(10) + + +def update_button_c(button, button_state, pwr, mode): + """Surveille le bouton C (clic central)""" + global PWROFF_DELAY + + if button.value() == 0: + # print("appui") + if button_state == 1: + # Deadline avant de changer l'état d'allumage + PWROFF_DELAY = time.ticks_add(time.ticks_ms(), 2000) + # print(PWROFF_DELAY) + else: + # Est-ce que la deadline est atteinte ? si oui on change l'état + # d'allumage + # print(PWROFF_DELAY, "/", time.ticks_ms()) + if time.ticks_diff(time.ticks_ms(), PWROFF_DELAY) >= 0: + PWROFF_DELAY = 0 + power_cycle() + else: + if pwr and button_state == 0: + # Si on a juste un clic rapide sur le bouton, on change de mode au + # relachement du bouton + mode = mode + 1 + print("Mode : {}/{}".format(CURRENT_MODE, + len(light_modes.MODES_LST))) + if mode >= len(light_modes.MODES_LST): + mode = 0 + + return mode + + +def update_wifi_led(): + """Refresh Wifi status LED""" + global WIFI_LED + global WLAN + WIFI_LED = WLAN.isconnected() + ##################### # BOUCLE PRINCIPALE # @@ -109,32 +146,20 @@ def update_neopixel(np_strp, col, bri=50): while True: - # Si on a un changement de valeur, on met à jour la luminisité et l'éclairage - # des NeoPixel - if PWR and BRIGHTN != ENCODER.value: - print(ENCODER.value) - BRIGHTN = ENCODER.value - update_neopixel(NPXL_STRIP, COLOR, BRIGHTN) + # Si on a un changement de valeur, on met à jour la luminosité + if PWR: + if BRIGHTN != ENCODER.value: + print(ENCODER.value) + BRIGHTN = ENCODER.value - # Quand le bouton central est appuyé puis relâché, on allume ou on éteint - if ENC_BUT.value() == 0 and BUTTN_STATE == 1: - PWR = not PWR - print("Power :", PWR) - if not PWR: - # Extinction des LED - NPXL_STRIP.fill((0, 0, 0)) - NPXL_STRIP.write() - else: - # Luminité basse si on allume avec une luminisité de 0 - if BRIGHTN == 0: - BRIGHTN = 10 + # Mise à jour des LED + light_modes.update_neopixel(CURRENT_MODE, + NPXL_STRIP, + USR_COLOR, BRIGHTN) - # On remet l'encodeur à la dernière luminosité connue - # pour ignorer les mouvements pendant l'extinction - ENCODER.reset(BRIGHTN) - # Rallume les LED: - update_neopixel(NPXL_STRIP, COLOR, BRIGHTN) + # Si on laisse appuyer 2 secondes: extinction + CURRENT_MODE = update_button_c(ENC_BUT, + BUTTN_STATE, + PWR, CURRENT_MODE) BUTTN_STATE = ENC_BUT.value() - - time.sleep_ms(20) diff --git a/pymakr.conf b/pymakr.conf deleted file mode 100644 index 427deeb..0000000 --- a/pymakr.conf +++ /dev/null @@ -1,10 +0,0 @@ -{ - "address": "/dev/ttyUSB0", - "username": "", - "password": "", - "sync_folder": "code", - "sync_file_types": "py,txt,log,json,xml,html,js,css,mpy,pem,cet,crt,key", - "sync_all_file_types": true, - "open_on_start": true, - "safe_boot_on_upload": false -} diff --git a/pymakr_wifi.conf b/pymakr_wifi.conf deleted file mode 100644 index 3628488..0000000 --- a/pymakr_wifi.conf +++ /dev/null @@ -1,10 +0,0 @@ -{ - "address": "192.168.1.20", - "username": "", - "password": "frE3d0M4", - "sync_folder": "code", - "sync_file_types": "py,txt,log,json,xml,html,js,css,mpy,pem,cet,crt,key", - "sync_all_file_types": true, - "open_on_start": true, - "safe_boot_on_upload": false -}