diff --git a/micropython/examples/plasma_stick/cheerlights.py b/micropython/examples/plasma_stick/cheerlights.py new file mode 100644 index 00000000..b25183e5 --- /dev/null +++ b/micropython/examples/plasma_stick/cheerlights.py @@ -0,0 +1,79 @@ +import WIFI_CONFIG +from network_manager import NetworkManager +import uasyncio +from urequests import get +import time +import ujson +import plasma +from plasma import plasma2040 + +""" +This Plasma Stick example sets your LED strip to the current #cheerlights colour. +Find out more about the Cheerlights API at https://cheerlights.com/ +""" + +URL = 'http://api.thingspeak.com/channels/1417/field/2/last.json' +UPDATE_INTERVAL = 120 # refresh interval in secs. Be nice to free APIs! + +# Set how many LEDs you have +NUM_LEDS = 50 + + +def status_handler(mode, status, ip): + # reports wifi connection status + print(mode, status, ip) + print('Connecting to wifi...') + # flash while connecting + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 255, 255, 255) + time.sleep(0.02) + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 0, 0, 0) + if status is not None: + if status: + print('Connection successful!') + else: + print('Connection failed!') + # light up red if connection fails + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 255, 0, 0) + + +def hex_to_rgb(hex): + # converts a hex colour code into RGB + h = hex.lstrip('#') + r, g, b = (int(h[i:i + 2], 16) for i in (0, 2, 4)) + return r, g, b + + +# set up the WS2812 / NeoPixel™ LEDs +led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT) + +# start updating the LED strip +led_strip.start() + +# set up wifi +network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) +uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + +while True: + # open the json file + print(f"Requesting URL: {URL}") + data = get(URL).json() + print("Data obtained!") + + # extract hex colour from the data + hex = data['field2'] + + # and convert it to RGB + r, g, b = hex_to_rgb(hex) + + # light up the LEDs + for i in range(NUM_LEDS): + led_strip.set_rgb(i, r, g, b) + print(f"LEDs set to {hex}") + + # sleep + print(f"""Sleeping for {UPDATE_INTERVAL} seconds. + """) + time.sleep(UPDATE_INTERVAL) \ No newline at end of file diff --git a/micropython/examples/plasma_stick/encoder.py b/micropython/examples/plasma_stick/encoder.py new file mode 100644 index 00000000..00552c8e --- /dev/null +++ b/micropython/examples/plasma_stick/encoder.py @@ -0,0 +1,81 @@ +from pimoroni_i2c import PimoroniI2C +from breakout_encoder import BreakoutEncoder +import plasma +from plasma import plasma2040 + +""" +Change the colour of your LEDs easily by hooking up an RGB Encoder Breakout! +""" + +# set how many LEDs you have +NUM_LEDS = 50 + +# make this number bigger for more precise colour adjustments +STEPS_PER_REV = 24 + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +enc = BreakoutEncoder(i2c) + +enc.set_brightness(1.0) +# enc.set_direction(BreakoutEncoder.DIRECTION_CCW) # Uncomment this to flip the direction + + +def hsv_to_rgb(h, s, v): + # From CPython Lib/colorsys.py + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +def count_changed(count): + print("Count: ", count, sep="") + h = ((count % STEPS_PER_REV) * 360.0) / STEPS_PER_REV # Convert the count to a colour hue + r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic + # set the encoder LED colour + enc.set_led(r, g, b) + # set the led strip to match + for i in range(NUM_LEDS): + led_strip.set_rgb(i, r, g, b) + + +# WS2812 / NeoPixel™ LEDs +led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT) + +# Start updating the LED strip +led_strip.start() + +count = 0 + +count_changed(count) + +enc.clear_interrupt_flag() + +while True: + if enc.get_interrupt_flag(): + count = enc.read() + enc.clear_interrupt_flag() + + while count < 0: + count += STEPS_PER_REV + + count_changed(count) + \ No newline at end of file diff --git a/micropython/examples/plasma_stick/thermometer.py b/micropython/examples/plasma_stick/thermometer.py new file mode 100644 index 00000000..18287144 --- /dev/null +++ b/micropython/examples/plasma_stick/thermometer.py @@ -0,0 +1,47 @@ +import plasma +from plasma import plasma2040 +from pimoroni import RGBLED +from pimoroni_i2c import PimoroniI2C +import machine +import time + +""" +Reads the internal temperature sensor on the Pico and changes the LED strip an appropriate colour. +""" + +# Set how many LEDs you have +NUM_LEDS = 50 + +BRIGHTNESS = 1.0 + +MIN = 15 +MAX = 30 + +# What you want your MIN colour to be - a hue between 0 and 360 degrees. +# Green is 120! +START_HUE = 120 + +# WS2812 / NeoPixel™ LEDs +led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT, rgbw=False) + +# Start updating the LED strip +led_strip.start() + +sensor_temp = machine.ADC(4) +conversion_factor = 3.3 / (65535) # used for calculating a temperature from the raw sensor reading + +while True: + + # the following two lines do some maths to convert the number from the temp sensor into celsius + reading = sensor_temp.read_u16() * conversion_factor + temperature = 27 - (reading - 0.706) / 0.001721 + print(f""" + Temperature: {temperature:0.2f} * C + """) + + # calculates a colour + hue = max(0, START_HUE / 360 * (1 - (temperature - MIN) / MAX)) + for i in range(NUM_LEDS): + led_strip.set_hsv(i, hue, 1.0, BRIGHTNESS) + + time.sleep(0.5) diff --git a/micropython/examples/plasma_stick/weather.py b/micropython/examples/plasma_stick/weather.py new file mode 100644 index 00000000..e8b9f96e --- /dev/null +++ b/micropython/examples/plasma_stick/weather.py @@ -0,0 +1,237 @@ +import WIFI_CONFIG +from network_manager import NetworkManager +import uasyncio +from urequests import get +import time +import ujson +import plasma +from plasma import plasma2040 +# Random functions! randrange is for picking integers from a range, and uniform is for floats. +from random import randrange, uniform +from machine import Timer +import gc + +""" +Weather in a bottle! +This Plasma Stick example connects to Open Meteo to access the current weather conditions. +It then does some cool weather appropriate stuff with LEDs. +Find out more about the Open Meteo API at https://open-meteo.com +Based on original code by AxWax <3 https://github.com/axwax/Open-Meteo-Inky-Pack +""" + +# Set how many LEDs you have +NUM_LEDS = 50 + +# Set your latitude/longitude here (find yours by right clicking in Google Maps!) +LAT = 53.38609085276884 +LNG = -1.4239983439328177 +TIMEZONE = "auto" # determines time zone from lat/long + +URL = "https://api.open-meteo.com/v1/forecast?latitude=" + str(LAT) + "&longitude=" + str(LNG) + "¤t_weather=true&timezone=" + TIMEZONE +UPDATE_INTERVAL = 500 # refresh interval in secs. Be nice to free APIs! + +# Weather codes from https://open-meteo.com/en/docs#:~:text=WMO%20Weather%20interpretation%20codes%20(WW) +WEATHERCODES = { + 0: 'clear sky', + 1: 'mostly clear', + 2: 'partly cloudy', + 3: 'cloudy', + 45: 'fog and depositing rime', + 48: 'fog', + 51: 'light drizzle', + 53: 'moderate drizzle', + 55: 'dense drizzle', + 56: 'light freezing drizzle', + 57: 'dense freezing drizzle', + 61: 'slight rain', + 63: 'moderate rain', + 65: 'heavy rain', + 66: 'light freezing rain', + 67: 'heavy freezing rain', + 71: 'slight snow', + 73: 'moderate snow', + 75: 'heavy snow', + 77: 'snow grains', + 80: 'slight rain showers', + 81: 'moderate rain showers', + 82: 'violent rain showers', + 85: 'slight snow showers', + 86: 'heavy snow showers', + 95: 'thunderstorm', + 96: 'thunderstorm with slight hail', + 99: 'thunderstorm with heavy hail' +} + + +def status_handler(mode, status, ip): + # reports wifi connection status + print(mode, status, ip) + print('Connecting to wifi...') + # flash while connecting + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 255, 255, 255) + time.sleep(0.02) + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 0, 0, 0) + if status is not None: + if status: + print('Connection successful!') + else: + print('Connection failed!') + # light up red if connection fails + for i in range(NUM_LEDS): + led_strip.set_rgb(i, 255, 0, 0) + +def get_data(): + global weathercode + # open the json file + print(f"Requesting URL: {URL}") + j = get(URL).json() + print("Data obtained!") + + # parse relevant data from JSON + current= j["current_weather"] + temperature = current["temperature"] + weathercode = current["weathercode"] + datetime_arr = current["time"].split("T") + + print(f""" + Temperature = {temperature}°C + Conditions = {WEATHERCODES[weathercode]} + Last Open-Meteo update: {datetime_arr[0]}, {datetime_arr[1]} + """) + + gc.collect() + +# the rest of our functions are for animations! +def display_current(): + # paint our current LED colours to the strip + for i in range(NUM_LEDS): + led_strip.set_rgb(i, current_leds[i][0], current_leds[i][1], current_leds[i][2]) + +def move_to_target(): + # nudge our current colours closer to the target colours + for i in range(NUM_LEDS): + for c in range(3): # 3 times, for R, G & B channels + if current_leds[i][c] < target_leds[i][c]: + current_leds[i][c] = min(current_leds[i][c] + ANIMATION_SPEED, target_leds[i][c]) # increase current, up to a maximum of target + elif current_leds[i][c] > target_leds[i][c]: + current_leds[i][c] = max(current_leds[i][c] - ANIMATION_SPEED, target_leds[i][c]) # reduce current, down to a minimum of target + +def clear(): + # nice sunny yellow + for i in range(NUM_LEDS): + target_leds[i] = [242, 237,80] + +def clouds(): + # base colours: + if weathercode == 2: + cloud_colour = [165, 168, 138] # partly cloudy + if weathercode == 3: + cloud_colour = [93, 94, 83] # cloudy + if weathercode in (45, 48): + cloud_colour = [186, 185, 182] # foggy + + # add highlights and lowlights + for i in range(NUM_LEDS): + if uniform(0, 1) < 0.001: # highlight + target_leds[i] = [x+20 for x in cloud_colour] + elif uniform(0, 1) < 0.001: # lowlight + target_leds[i] = [x-20 for x in cloud_colour] + elif uniform(0, 1) < 0.005: # normal + target_leds[i] = cloud_colour + +def storm(): + # heavy rain, with lightning! + raindrop_chance = 0.01 + + for i in range(NUM_LEDS): + if raindrop_chance > uniform(0, 1): + # paint a raindrop (use current rather than target, for an abrupt change to the drop colour) + current_leds[i] = [randrange(0, 50), randrange(20, 100), randrange(50, 255)] + else: + # paint backdrop + target_leds[i] = [0, 15, 60] + + lightning_chance = 0.001 + if lightning_chance > uniform(0, 1): + for i in range(NUM_LEDS): + current_leds[i] = [255, 255, 255] + +def rain(): + # splodgy blues + # first, work out how many raindrops: + if weathercode in (51, 56, 61, 66, 80): # light rain + raindrop_chance = 0.001 + elif weathercode in (53, 63, 81): #moderate rain + raindrop_chance = 0.005 + else: #heavy rain + raindrop_chance = 0.01 + + for i in range(NUM_LEDS): + if raindrop_chance > uniform(0,1): + # paint a raindrop (use current rather than target, for an abrupt change to the drop colour) + current_leds[i] = [randrange(0, 50), randrange(20, 100), randrange(50, 255)] + else: + # paint backdrop + target_leds[i] = [0, 15, 60] + +def snow(): + # splodgy whites + # first, work out how many snowflakes: + if weathercode in (71, 85): # light snow + snowflake_chance = 0.001 + elif weathercode in (73, 77): # moderate snow + snowflake_chance = 0.005 + else: #heavy snow + snowflake_chance = 0.01 + + for i in range(NUM_LEDS): + if snowflake_chance > uniform(0, 1): + # paint a snowflake (use current rather than target, for an abrupt change to the drop colour) + current_leds[i] = [227, 227, 227] + else: + # paint backdrop + target_leds[i] = [54, 54, 54] + +# some variables we'll use for animations +ANIMATION_SPEED = 2 # higher number gets from current to target colour faster + +current_leds = [ [0] * 3 for i in range(NUM_LEDS)] # Create an list of [r, g, b] values that will hold current LED colours, for display +target_leds = [ [0] * 3 for i in range(NUM_LEDS)] # Create an list of [r, g, b] values that will hold target LED colours, to move towards + +# set up the WS2812 / NeoPixel™ LEDs +led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT) + +# start updating the LED strip +led_strip.start() + +# set up wifi +network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) +uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + +# get the first lot of data +get_data() + +# start timer (the timer will update our data every UPDATE_INTERVAL) +timer = Timer(-1) +timer.init(period=UPDATE_INTERVAL*1000, mode=Timer.PERIODIC, callback=lambda t:get_data()) + +while True: + # do some fancy stuff with the LEDs based on the weather code + if 0 <= weathercode <= 1: + clear() + elif 2 <= weathercode <= 48: + clouds() + elif 51 <= weathercode <= 67 or 80 <= weathercode <= 82: + rain() + elif 71 <= weathercode <= 77 or 85 <= weathercode <= 86: + snow() + elif 95 <= weathercode <= 99: + storm() + else: + print("Unknown weather code :(") + + move_to_target() # nudge our current colours closer to the target colours + display_current() # display current colours to strip + \ No newline at end of file