Showing posts with label Plasma Stick. Show all posts
Showing posts with label Plasma Stick. Show all posts

Sunday, 29 December 2024

Pimoroni Plasma 2350

Raspberry Pi recently released the RP2350 microcontroller. There was a shortage of the corresponding Pico 2 development board, but other board manufacturers certainly filled the gap.

One of them was Pimoroni with a number of interesting offerings.

One of them is the Plasma 2350 Neopixel strip controller.

I already had a set of the RGB LED Star Wire 66 Neopixel strips, so I just bought the board.

The board is fully assembled and just requires the use of a jeweller's type screwdriver to attach the wires from the NeoPixel strip to the board using the terminal block. One thing to note is that unlike the Plasma Stick 2040W, the Plasma 2350 has a four connector terminal block. As you can see, the connections to use are 5V, Dat[a] and negative.

The board has one QWIC/STEMMA I2C connector, an on-board NeoPixel, three buttons (reset, boot and user button) and a connector labelled SP/CE. The latter is an SPI connector standard allowing higher speed connections to the board (such as WIFI).

What it does not have is WIFI or Bluetooth onboard (a later variant does but does not have the SP/CE connector, a Raspberry Pi RM2 Wireless module is directly wired to the board.)

Code

So to use the board and the LED string, some code is required.
This is the code for the main.py file.
The plasma2040 module in plasma works with the Pasma 2350.
The user interface is very simple. Pressing the User Button A cycles through the available light options.
The specific display uses Pythons' polymorphism to handle the basic operations. The method update() updates the display and is triggered (in this case) every half a second.
The light_show_base class (and its subtypes) determine what happens during the update. That code is in a separate file.

main.py

import plasma
from light_show_base import RGBElement, light_show_base, light_show_static_seven, light_show_static_seven_reverse, light_show_alternate
from plasma import plasma2040
from time import sleep
from pimoroni import RGBLED
# Set how many LEDs you have
NUM_LEDS = 66

led = RGBLED(16, 17, 18) 

# WS2812 / NeoPixel™ LEDs
led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT)

# Start updating the LED strip
led_strip.start()

display = light_show_base(led_strip, NUM_LEDS, led)
a_button = machine.Pin(12, machine.Pin.IN, machine.Pin.PULL_UP)

selected_pattern = 0

while True:
    display.update()
    if a_button.value() == 0:
        selected_pattern = selected_pattern + 1
        if selected_pattern > 5:
            selected_pattern = 0

        if selected_pattern == 0:
            display = light_show_base(led_strip, NUM_LEDS, led)
        elif selected_pattern == 1:
            display = light_show_static_seven(led_strip, NUM_LEDS, led)
        elif selected_pattern == 2:
            display = light_show_static_seven_reverse(led_strip, NUM_LEDS, led)
        elif selected_pattern == 3:
            display = light_show_alternate(led_strip, NUM_LEDS, led, RGBElement(255,0,0), RGBElement(0,255,0))
        elif selected_pattern == 4:
            display = light_show_alternate(led_strip, NUM_LEDS, led, RGBElement(0,0,255), RGBElement(255,255,0))
        elif selected_pattern == 5:
            display = light_show_alternate(led_strip, NUM_LEDS, led, RGBElement(0,255,0), RGBElement(255,255,255))
        print(selected_pattern)

    sleep(0.5)
    

light_show_base.py

The file contains two main classes: RGBElement and light_show_base.
There are also subclasses of class light_show_base, inheriting from light_show_base.
class RGBElement:
    def __init__(self,red, green,blue):
        self.red = red
        self.green = green
        self.blue = blue
    def __str__(self): 
        return f"{self.red} {self.green} {self.blue}"
        
class light_show_base:
    def __init__(self, led_strip, num_leds, rgb_led):
        # Device specific settings
        self.led_strip = led_strip
        self.rgb_led = rgb_led
        self.num_leds = num_leds
        
        # Chosen colour
        self.current_colour = 0
        
        # Base set of colours
        self.white = RGBElement(255,255,255)
        self.yellow = RGBElement(255,255,0)
        self.magenta = RGBElement(255,0,255)
        self.red = RGBElement(255,0,0)
        self.cyan = RGBElement(0,255,255)
        self.green = RGBElement(0,255,0)
        self.blue = RGBElement(0,0,255)
        self.black = RGBElement(0,0,0)
  
        # List of colours
        self.colours = [
            self.white,
            self.yellow,
            self.magenta,
            self.red,
            self.cyan,
            self.green,
            self.blue,
            self.black
            ] 

            
    def update(self):
        if self.current_colour == len(self.colours) - 1:
            self.current_colour = 0
        else:
            self.current_colour = self.current_colour + 1

        rgb_colour = self.colours[self.current_colour]
        self.rgb_led.set_rgb(rgb_colour.red, rgb_colour.green, rgb_colour.blue)
        for i in range(self.num_leds):
            self.led_strip.set_rgb(i, rgb_colour.green,rgb_colour.red, rgb_colour.blue)
        
class light_show_static_seven(light_show_base):
    def __init__(self, led_strip, num_leds, rgb_led):
        super().__init__(led_strip, num_leds, rgb_led)
        self.colour_strip = []
        ix = 0
        for i in range(self.num_leds):
            self.colour_strip.append(self.colours[ix])
            if ix == len(self.colours) - 1:
                ix = 0
            else:
                ix = ix +1
                
    def change_colours(self):
        last_colour = self.colour_strip[1]
        for i in range(self.num_leds):
            if i > 1:
                self.colour_strip[i - 1] = self.colour_strip[i]
        self.colour_strip[self.num_leds - 1] = last_colour
            
    def update(self):
        for i in range(self.num_leds):
            rgb_colour = self.colour_strip[i]           
            self.led_strip.set_rgb(i, rgb_colour.green,rgb_colour.red, rgb_colour.blue)
        rgb_colour = self.colour_strip[1] 
        self.rgb_led.set_rgb(rgb_colour.red, rgb_colour.green, rgb_colour.blue)

        self.change_colours()

class light_show_static_seven_reverse(light_show_static_seven):
    def __init__(self, led_strip, num_leds, rgb_led):
        super().__init__(led_strip, num_leds, rgb_led)

    def change_colours(self):
        last_colour = self.colour_strip[self.num_leds-1] 
        for i in range(self.num_leds-1):
            ix = self.num_leds - i
            print(ix)
            if ix < self.num_leds-1: 
                self.colour_strip[ix + 1] = self.colour_strip[ix]
        self.colour_strip[1] = last_colour            
        
class light_show_alternate(light_show_base):
     def __init__(self, led_strip, num_leds, rgb_led, led_one, led_two):
        super().__init__(led_strip, num_leds, rgb_led)
        self.led_one = led_one
        self.led_two = led_two

     def change_colours(self):
        temp = self.led_one
        self.led_one = self.led_two
        self.led_two = temp


     def update(self):
        led_one = self.led_one
        led_two = self.led_two
        self.rgb_led.set_rgb(led_one.red, led_one.green, led_one.blue)
        for i in range(self.num_leds/2):
            self.led_strip.set_rgb(i * 2, led_one.green,led_one.red, led_one.blue)
            self.led_strip.set_rgb(i * 2+1, led_two.green,led_two.red, led_two.blue)
        self.change_colours()

References


Tuesday, 20 December 2022

PIMORONI Plasma Stick 2040 W Christmas Lights

 So having played around with the Plasma Stick 2040 and run a web server on it, it was time to use it for Christmas lights

Initially the idea was to use the PICO W as an Access Point and control it via a web interface. This would make it independent of any local access point. Unfortunately there were some issues which I was unable to resolve before I needed to use the lights in the field. So to be able to change the lights a Rotary Encoder was plugged into the QWIIC socket via a Breakout Garden to QWIIC converter and a QWIIC cable.

For Christmas tree lights of course, the existing web server could have been used, time waits for no developer so it was used "as is".

In addition there is a Pimoroni Snowflake Solo on the tree.





Sunday, 13 November 2022

PIMORONI Plasma Stick 2040 W web site controlled colour

The PIMORONI Plasma Stick 2040 W or Wireless Plasma Kit is a Raspberry Pi PICO controlled  LED controller for 5V WS2812/Neopixel/SK6812 LED strips.

The PICO W has built in wireless support which can be used to host a limited web site. A full web stack probably exceeds the capabilities of the microcontroller but by using a (semi) RESTFul web server it is possible to control the LED strips via a web interface.

AJAX

Asynchronous Javascript And XML (AJAX) uses a combination of web technologies to create asynchronous web applications client-side. It has a long history, and initially required a quite complicated set up. As browser (client-side) support has expanded, it has become much easier to set up responsive web applications that do not require the whole page to be updated.

Though XML is part of the name, the returned data can be anything – in this case the returned data will be JSON.

Client-Side UI

The user interface needs to be simple as it is provided by the PICO.

<!DOCTYPE html>
<html>
    <head>
        <title>AJAX-PICO test frame</title>
        <script lang="JScript">
            function sendData(slideAmount,field)
            {
                // Create an XMLHttpRequest object
                const xhttp = new XMLHttpRequest();
                // Define a callback function
                xhttp.onload = function() {
                    // Get response as JSON
                    var returnType = xhttp.getResponseHeader("Content-Type");
                    //alert(returnType);
                    if(returnType =="application/json"){
                        var returnText = xhttp.responseText;
                        const obj = JSON.parse(returnText);
                        // Get fieldname and corresponding HTML element
                        var fieldname = obj.field;
                        //alert(fieldname);
                        var field = document.getElementById(fieldname);
                        if(field != null){
                            field.innerHTML = obj.value;
                        }
                        else{
                            alert("Field not found - JSON - " + returnText);
                        }
                    }
                }
                // Send a request
                xhttp.open("GET", "plasma2040/" + field +"/"+ slideAmount);
                xhttp.send();
            }
        </script>
    </head>
    <body>
        <div>
            <div>
                <input type="range" id="Red"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnRed')">
            </div>
            <div>
                <span id="returnRed">%d</span>
            </div>
            <div>
                <input type="range" id="Green"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnGreen')">
            </div>
            <div>
                <span id="returnGreen">%d</span>
            </div>
            <div>
                <input type="range" id="Blue"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnBlue')">
            </div>
            <div>
                <span id="returnBlue">%d</span>
            </div>
        </div>
    </body>
</html>


Three Input Range controls and three Spans for the current values.

The three Range controls have an OnChange event defined, which makes a call to a JScript function with the field name and the value of the range control.
The JScript function creates an XMLHTTP object, adds a call back method and sends the request to the server including the field name and its value as a RESTFul URL.
The call back function expects a valid JSON string of the form:

{"field":"< field name>","value":"<value>"}

For example: {"field":"returnRed","value":"128"}
The string is converted into a Jscript object.
The field name is extracted from the object and is  used to identify the Span object to receive the new value, also extarcted from the object.

Server Side

The server-side code is derived from the asynchronous web server example from Connecting to the Internet with Raspberry Pi Pico W.


The HTML string contains the returned web page, with six numeric placeholders (which are replaced with pairs of the three colours).


The built-in LED and the LED strip are set up and a function to set all the LEDs to the current red, green and blue settings created.


A function to connect to the network is created.


The web server function detects if the request is an AJAX request to set a colour or for the controlling web page.


If it is an AJAX request, the calling URL is obtained from the request string, and the sub-parts of the URL are extracted. Element one is the field name, element two is the field value.
The field names are then checked against Red, Green and Blue and the corresponding variables set to the new values.


A JSON string is constructed and returned to the calling browser.


If the request is not to handle an AJAX request, the HTML is returned with the current red, green and blue values set for the Range controls and the span.


Source Code

# The asynchronous web server code is derived from the Raspberry Pi document:
# "Connecting to the Internet with Raspberry Pi Pico W"
# Imports
import network
import socket
import time
from machine import Pin
import uasyncio as asyncio
import plasma
from plasma import plasma_stick
import secrets
# Default page settings
html = """<!DOCTYPE html>
<html>
    <head>
        <title>AJAX-PICO test frame</title>
        <script lang="JScript">
            function sendData(slideAmount,field)
            {
                // Create an XMLHttpRequest object
                const xhttp = new XMLHttpRequest();
                // Define a callback function
                xhttp.onload = function() {
                    // Get response as JSON
                    var returnType = xhttp.getResponseHeader("Content-Type");
                    //alert(returnType);
                    if(returnType =="application/json"){
                        var returnText = xhttp.responseText;
                        const obj = JSON.parse(returnText);
                        // Get fieldname and corresponding HTML element
                        var fieldname = obj.field;
                        //alert(fieldname);
                        var field = document.getElementById(fieldname);
                        if(field != null){
                            field.innerHTML = obj.value;
                        }
                        else{
                            alert("Field not found - JSON - " + returnText);
                        }
                    }
                }
                // Send a request
                xhttp.open("GET", "plasma2040/" + field +"/"+ slideAmount);
                xhttp.send();
            }
        </script>
    </head>
    <body>
        <div>
            <div>
                <input type="range" id="Red"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnRed')">
            </div>
            <div>
                <span id="returnRed">%d</span>
            </div>
            <div>
                <input type="range" id="Green"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnGreen')">
            </div>
            <div>
                <span id="returnGreen">%d</span>
            </div>
            <div>
                <input type="range" id="Blue"  min="0" max="255" step="1" value="%d" onchange="sendData(this.value, 'returnBlue')">
            </div>
            <div>
                <span id="returnBlue">%d</span>
            </div>
        </div>
    </body>
</html>
"""

# Set up built in LED
led = Pin(15, Pin.OUT)
onboard = Pin("LED", Pin.OUT, value=0)
# Set up LED strip
NUM_LEDS = 50
red = 128
blue = 128
green = 128
# WS2812 / NeoPixel™ LEDs
led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma_stick.DAT, color_order=plasma.COLOR_ORDER_RGB)
# Start updating the LED strip
led_strip.start()
# Set all LEDs to current red, green and blue values
def set_all_leds():
    global red
    global green
    global blue
    for i in range(NUM_LEDS):
        led_strip.set_rgb(i, red,green,blue)    
# Set all the LEDs
set_all_leds()
# Connect to network
wlan = network.WLAN(network.STA_IF)
def connect_to_network():
    wlan.active(True)
    wlan.config(pm = 0xa11140)  # Disable power-save mode
    wlan.connect(secrets.SSID, secrets.PASSWORD)
    max_wait = 10
    while max_wait > 0:
        if wlan.status() < 0 or wlan.status() >= 3:
            break
        max_wait -= 1
        print('waiting for connection...')
        time.sleep(1)
    if wlan.status() != 3:
        raise RuntimeError('network connection failed')
    else:
        print('connected')
        status = wlan.ifconfig()
        print('ip = ' + status[0])
async def serve_client(reader, writer):
    global red
    global green
    global blue
    print("Client connected")
    request_line = await reader.readline()
    print("Request:", request_line)
    # We are not interested in HTTP request headers, skip them
    while await reader.readline() != b"\r\n":
        pass
    request = str(request_line)
    # If the request contains plasma2040 then it should also
    # contain a colour change request - plasma2040/<colour field>/<colour value>
    if request.find('plasma2040/')>0:
        print("Plasma 2040 path")
        # Slice the RESTFul path out of the request
        start = request.find('plasma2040/')
        end = request.find('HTTP')
        path = request[start:end]
        # Split the path
        subpaths = path.split('/')
        # If there are the required parts
        if(len(subpaths)>2):
            #Check for each colour in turn
            if(subpaths[1].find("Red")>0):
                red = int(subpaths[2])
            if(subpaths[1].find("Green")>0):
                green = int(subpaths[2])
            if(subpaths[1].find("Blue")>0):
                blue = int(subpaths[2])
            # Update the LEDs
            set_all_leds()
            # Send back the JSON with the new colour settings
            writer.write('HTTP/1.0 200 OK\r\nContent-type: application/json\r\n\r\n')
            response = '{"field":"'+subpaths[1]+'","value":"'+subpaths[2]+'"}'
            writer.write(response)
    else:
        # If not Plasma2040 send back the HTML
        response = html % (red, red, green, green, blue, blue)
        writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
        writer.write(response)
    await writer.drain()
    await writer.wait_closed()
    print("Client disconnected")
async def main():
    print('Connecting to Network...')
    connect_to_network()
    print('Setting up webserver...')
    asyncio.create_task(asyncio.start_server(serve_client, "0.0.0.0", 80))
    while True:
        onboard.on()
        print("heartbeat")
        await asyncio.sleep(0.25)
        onboard.off()
        await asyncio.sleep(5)
        
try:
    asyncio.run(main())
finally:
    asyncio.new_event_loop()

References

https://shop.pimoroni.com/products/plasma-stick-2040-w?variant=40359072301139

https://shop.pimoroni.com/products/wireless-plasma-kit?variant=40372594704467

https://learn.pimoroni.com/article/getting-started-with-pico

https://learn.pimoroni.com/article/assembling-wireless-plasma-kit

https://datasheets.raspberrypi.com/picow/connecting-to-the-internet-with-pico-w.pdf

https://en.wikipedia.org/wiki/Ajax_(programming)

https://www.w3schools.com/js/js_ajax_http_response.asp

https://www.w3schools.com/js/js_json_parse.asp

http://json.parser.online.fr/


 

Saturday, 5 November 2022

PIMORONI Plasma Stick 2040 W

 

PIMORONI Plasma Stick 2040 W or Wireless Plasma Kit is an LED controller for 5V WS2812/Neopixel/SK6812 LED strips.

It is part of PIMORONI's "Pico W Aboard" range which has a Raspberry Pi Pico W soldered to the board. The board includes a RESET button (saving wear and tear as you do not have to keep unplugging and plugging the USB), a three socket screw terminal for the LED strip and a Qwiic/STEMMA QT connector to add various sensors.
Pimoroni have a quick start guide plus Micropython and C++ libraries.