Skip to content

ports/esp32/boards/PYBOX: Add support SparkFun Thing Plus board. #10324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions ports/esp32/boards/PYBOX/board.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"deploy": [
"../deploy.md"
],
"docs": "",
"features": [
"Battery Charging",
"Feather",
"RGB LED",
"STEMMA QT/QWIIC",
"BLE",
"WiFi"
],
"id": "pyBox",
"images": [
"pyBox.jpg"
],
"mcu": "esp32",
"product": "pyBox",
"thumbnail": "https://wolfpaulus.com/content/pyBox.jpg",
"url": "https://wolfpaulus.com/pyBox",
"vendor": "Techcasita Productions"
}
2 changes: 2 additions & 0 deletions ports/esp32/boards/PYBOX/board.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The following files for the SparkFun Thing Plus – ESP32 WROOM (USB-C) paired with the SparkFun 16×2 SerLCD – RGB Text (Qwiic).
This firmware is compiled using ESP-IDF v4.4.
2 changes: 2 additions & 0 deletions ports/esp32/boards/PYBOX/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include("$(PORT_DIR)/boards/manifest.py")
freeze("modules")
148 changes: 148 additions & 0 deletions ports/esp32/boards/PYBOX/modules/pybox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""
NeoPixel, BlueLED, SDDrive, WiFi for MicroPython on SparkFun Thing Plus
Author: wolf@paulus.com
MIT license; Copyright (c) 2022 wolfpaulus.com
"""
from micropython import const
from machine import freq, Pin, SDCard, Signal, RTC
from utime import sleep
from network import WLAN, STA_IF
from neopixel import NeoPixel
from uos import mount
from pybox_btn import Button
from collections import OrderedDict

_BTN_UP_PIN = const(27)
_BTN_DN_PIN = const(33)
_TURBO_SPEED = const(240000000)
_NORMAL_SPEED = const(160000000)
_BLUE_LED_PIN = const(13)
_NEO_PIN = const(2)


class Neo:
"""
NeoPixel
"""

def __init__(self, pin: int = 2):
self.neo = NeoPixel(Pin(pin, Pin.OUT, value=False), 1)

def color(self, r: int = 255, g: int = 255, b: int = 255):
self.neo[0] = (r, g, b)
self.neo.write()

def get_color(self) -> (int, int, int):
return self.neo[0]

def off(self) -> None:
self.neo[0] = (0, 0, 0)
self.neo.write()


def turbo(on: bool = True) -> None:
"""
Switch ESP's clock speed
:param on: 240MHz is True else, defaulting to 160MHz
:return: None
"""
freq(_TURBO_SPEED if on else _NORMAL_SPEED)


def mount_sd(directory: str = "/sd") -> None:
"""
Define removable storage media - secure digital memory card
"""
sd = SDCard(slot=2, width=1, cd=None, wp=None, sck=18, miso=19, mosi=23, cs=5, freq=40000000)
mount(sd, directory)


def connect(wifi_networks: list) -> bool:
"""
Connect to Wifi router or become an Access Point
:param wifi_networks: list of tuples [(ssid, password),..]
:return: True if connected
"""
global hostname, ip_addr
conn = False
while not conn:
for ssid, pw in wifi_networks:
print(ssid)
sta_if = WLAN(STA_IF)
if not sta_if.isconnected():
sta_if.active(True)
sta_if.connect(ssid, pw)
# sta_if.config(dhcp_hostname=profile.HostName)
for i in range(2, 8):
sta_if = WLAN(STA_IF)
if sta_if.isconnected():
conn = True
ip_addr = sta_if.ifconfig()[0]
hostname = sta_if.config("dhcp_hostname")
break
else:
sleep(i)
if conn:
break
return conn


def is_connected() -> bool:
"""
:return: True is connected to WiFi
"""
return WLAN(STA_IF).isconnected()


def set_system_time(d: dict) -> None:
"""
Set the Systemtime
:param d: dictionary
:return:
"""
RTC().datetime((d["year"], d["month"], d["day"], 0, d["hour"], d["minute"], d["seconds"], 0))


def rgb_color() -> (int, int, int):
"""
RGB color generator (r,g,b in the range 0..255)
:return: (r,g,b all in the range 0..255)
"""
for i in range(1, 86):
yield 255 - i * 3, 0, i * 3
for i in range(1, 86):
yield 0, i * 3, 255 - i * 3
for i in range(1, 86):
yield i * 3, 255 - i * 3, 0


palette = OrderedDict(
[
# red
("Auriga", (255, 0, 63)),
("Antares", (255, 0, 0)),
# orange
("Jupiter", (255, 32, 0)),
("Proxima", (191, 128, 0)),
# gray
("Sirius", (255, 255, 255)),
("Vega", (127, 127, 191)),
# green
("Nebula", (0, 255, 0)),
("Kepler", (0, 127, 63)),
("Uranus", (0, 191, 255)),
# blue
("Rigel", (31, 31, 255)),
("Medusa", (127, 0, 255)),
("Orion", (255, 31, 255)),
]
)

blue_led = Signal(Pin(_BLUE_LED_PIN, mode=Pin.OUT, value=False), invert=False)
blue_led.off()
neo = Neo(_NEO_PIN)
neo.color(0, 0, 0)
btn_up = Button(_BTN_UP_PIN)
btn_dn = Button(_BTN_DN_PIN)
hostname = ""
ip_addr = "0.0.0.0"
70 changes: 70 additions & 0 deletions ports/esp32/boards/PYBOX/modules/pybox_btn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Capacitive Touch Buttons with Press and Long-Press detection
Author: Wolf Paulus wolf@paulus.com
MIT license; Copyright (c) 2022 wolfpaulus.com
"""
from machine import Pin, TouchPad
from utime import ticks_ms, ticks_diff
import pybox_ct as ct


class Button(TouchPad):
"""Capacitive Touch Button"""

def __init__(
self,
pin: int,
on_press: callable = None,
on_long_press: callable = None,
threshold: int = 600,
hold_ms: int = 500,
freq_ms: int = 100,
):
"""
:param pin: port pin this button is connected to
:param on_press: function to be called on touch
:param on_long_press: function to be called on long touch
:param threshold: capacitive touch value that qualifies as a touch
:param hold_ms: number of ms button needs to be touch to register a long touch
:param freq_ms: number of ms between checks
"""
super().__init__(Pin(pin))
self.on_press = on_press
self.on_long_press = on_long_press
self._threshold = threshold
self._hold = hold_ms
self._freq = freq_ms
self._acted = False
self._started = 0
self._active = True

def set_actions(self, on_press: callable = None, on_long_press: callable = None):
self.on_press = on_press
self.on_long_press = on_long_press

def check(self) -> None:
"""timer triggered"""
if self._active and (self.on_press or self.on_long_press):
v = self.read()
if v < self._threshold: # button currently touched
if not self._started:
self._started = ticks_ms()
elif self._hold < ticks_diff(ticks_ms(), self._started): # long press detected
if not self._acted:
self._active = False
self.on_long_press()
self._active = self._acted = True
elif self._started: # button was released
if not self._acted:
self._active = False
self.on_press()
self._active = True
else:
self._acted = False
self._started = 0

def enable(self):
ct.register((self.check, self._freq))

def disable(self):
ct.unregister((self.check, self._freq))
56 changes: 56 additions & 0 deletions ports/esp32/boards/PYBOX/modules/pybox_ct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
ct - Central Timer
One timer will be used for all of pyBox system calls, leaving remaining three for custom code
Author: Wolf Paulus wolf@paulus.com
MIT license; Copyright (c) 2022 wolfpaulus.com
"""
from micropython import const
from machine import Timer
import pybox_log as log

_TIMER_ID = const(0)
_TICK_MS = const(1)
MIN_FREQ = const(1000 // _TICK_MS * 60 * 60) # about 1 hour
MAX_FREQ = const(_TICK_MS)


def process(_) -> None:
"""
this is called every ${frequency}-th of a second and will call every callback that is due.
:return: None
"""
global _counter
_counter = _counter + 1 if _counter < MIN_FREQ else 1 # 1..3_600_000
for t in _tasks:
if _counter % t[1] == 0:
t[0]()


def register(task: ()) -> None:
"""
Registers a callback and call frequency. The callback should have no parameters
and the frequency should be in MAX_FREQ..MIN_FREQ range
:param task: callback,freq
:return: None
"""
_tasks.append(task)


def unregister(task: (callable, int)) -> None:
"""
Unregisters a previously registered task
:param task: callback,freq
:return: None
"""
for i in range(len(_tasks) - 1, -1, -1):
if _tasks[i][0].__name__ == task[0].__name__:
_tasks.pop(i)
break
else:
log.log(log.ERROR, f"Could not unregister {task[0].__name__}")


_counter = 0
_tasks = []
_timer = Timer(_TIMER_ID)
_timer.init(mode=Timer.PERIODIC, period=_TICK_MS, callback=process)
32 changes: 32 additions & 0 deletions ports/esp32/boards/PYBOX/modules/pybox_fg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
fg - Fuel Gauge
Author: Wolf Paulus wolf@paulus.com
MIT license; Copyright (c) 2022 wolfpaulus.com
"""
from micropython import const
import pybox_i2c as i2c

DEFAULT_ADDRESS = const(0x36)
_MAX17048_VCELL = const(0x02)
_MAX17048_SOC = const(0x04)


def is_connected(address=DEFAULT_ADDRESS):
"""
Determine if a device is connected to the system.
:return: True if the device is connected, otherwise False.
:rtype: bool
"""
return i2c.is_device_connected(address)


def voltage(address=DEFAULT_ADDRESS) -> float:
"""Current Voltage"""
raw = i2c.read16(address, _MAX17048_VCELL)
return raw * 78.125 / 1_000_000


def remaining(address=DEFAULT_ADDRESS) -> int:
"""remaining capacity in percentage"""
raw = i2c.read16(address, _MAX17048_SOC)
return raw // 256
Loading