Friendship Light

Overview

These two adorable lights are forever connected with the power of friendship. They connect to discord, and change to whatever color you change it to. You can then choose to send your friend the same color. It can also send morse code, and gradients. This project can be a little challenging if you dont have much experience with programming or electronics, but have no fear, I will guide you every step of the way.

Choose your lights


The lights I bought were a cat and a duck. Its important that your light be easy to take apart. I recommend buying a light that has replaceable batteries, so you don't have to worry about the wiring for charging.

Building and Wiring


1. Take lights apart

Carefully take your lights apart, avoid ripping or tearing the casing. Find the leds (typically attached to the circuit board) and take note of how they are arranged. Carefully remove the circuit board.

2. Create your board

To avoid space complications with the wires, I opted for soldering all components to a perf board.

You can buy these smart leds individually from adafruit. However, to save on money, I instead desoldered individual leds from the led strip I already had. If you want to do this as well, make sure your LED strip is "individually addressable" or "WS2812B". Lets look at the pin out of these lights:

Pinout

LED Chanin

Connect each LED in a chain like the diagram displayed above using solder bridges. My first 2 LED's look like this:

I then connected the raspi on the bottom of the board. Make a bridge from the first led to GND, pin 27, and 5V.

If your light is strangely shaped, or too small to hold the board down flat, you can extend wires from the pico.

If your light came with a switch, wire the battery to the switch and to power and ground on the pico. Make sure your pico isnt getting power from the battery while plugged in to usb!

You can also wire a usb cable to the pico and power it through a wall adapter.

Programming


The code for this project is written in micropython. There are many aspects of the code, responsible for different abilities we want the light to have.

Wifi:

Since the light needs to communicate, it has to be able to connect to the internet. This is why the raspi pico w is used for this project. The raspi pico w has a built in wifi module, so it can function as a device, and an access point for easy connection.

import network
import socket
import time
ap = 3
def con(ssid,password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, 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)

    return wlan.isconnected()

def write(ssid,password):
    file = open("secrets.txt", "w")
    file.write(f"{ssid}\n")
    file.write(f"{password}\n")
    file.close()

def connect():
    file = open("secrets.txt", "r")
    content = (file.read()).split("\n")
    file.close()
    return con(content[0].strip(),content[1].strip())

def AP_connect():
    ssid = 'Lumi'
    password = '123456789'

    ap = network.WLAN(network.AP_IF)
    ap.config(essid=ssid, password=password)
    ap.active(True)
    status = ap.ifconfig()
    print( 'ip = ' + status[0] )

def AP_disconnect():
    ap.disconnect()

Discord:

In order to communicate with the light, we use discord to give it commands. You can look at this as a creative implementation with a popular app, or a cheap work around for having to get a server and domain (take your pick). Discord's support of bots makes it super easy to read and send messages on a server.

Create a server for the project and create 3 chats: 1 for each light, and 1 as a shared chat. The command you share with your friends will go here.

Create a bot for each light. If you dont know how to make a discord bot, follow the tutorial here.

Now you can control the discord bots with the code here:

import WIFI
import network
import urequests
import ujson
WIFI.connect()
token = '[you bot token here]'
URL_GET_G = 'https://discord.com/api/v10/channels/{your channel ID}/messages?limit=1'
URL_POST_G = 'https://discord.com/api/v10/channels/{your channel ID}/messages'
ID = '5'
ID2 = '5'
URL_GET_S = 'https://discord.com/api/v10/channels/{your channel ID}/messages?limit=1'
URL_POST_S = 'https://discord.com/api/v10/channels/{your channel ID}/messages'

def send(URL, content):
    post_data = ujson.dumps({ 'content': content, 'embeds' : []})
    urequests.post(url = URL, headers={'Authorization': token,'content-type': 'application/json'}, data = post_data)

def getData(URL):
    global ID
    res = urequests.get(url=URL,headers={'Authorization': token})
    JSON = res.json()[0]
    if JSON['id'] != ID:
        ID = JSON['id']
        return JSON['content']
    else:
        return "none"

def start():
    global ID2
    res = urequests.get(url=URL_GET_S,headers={'Authorization': token})
    JSON = res.json()[0]
    ID2 = JSON['id']

def getDataS():
    global ID2
    res = urequests.get(url=URL_GET_S,headers={'Authorization': token})
    JSON = res.json()[0]
    if JSON['id'] != ID2:
        ID2 = JSON['id']
        return JSON['content']
    else:
        return "none"

Make sure to replace some of the fields with the right urls and id's of your bots.

LED control:

Last but not least, the raspi pico needs to send the commands from discord to the leds.

{.python} import network import socket import time import ujson import html import WIFI import website import colorwave as light import discord import colors import _thread con = False def blink(): global con light.fade_out() light.fill((255,0,0)) while con == False: if con == False: light.fade_in() if con == False: time.sleep(0.5) if con == False: light.fade_out() if con == False: time.sleep(0.5)

#_thread.start_new_thread(blink, ())

if WIFI.connect() == False:
    WIFI.AP_connect()
    website.sight()

current = (255, 150, 0)
command = "YELLOW"
baton = _thread.allocate_lock()



def morse():
    global color

    baton.acquire()
    text = command.strip("MORSE")
    print(text)
    c = color
    text = text.split(" ")
    while color == c:

        for let in text:
            for part in let:
                wait = 0
                print(part)
                if part == '.':
                    wait = 1
                    print("in .")

                elif part == '-' or part == '_':
                    wait = 3
                    print("in -")
                else:
                    break

                light.fill(current)
                time.sleep(wait)
                light.fill((0,0,0))
                print("black")
                time.sleep(1)
            time.sleep(2)
    baton.release()

def rainbow():
    global color
    baton.acquire()

    while color =="RAINBOW":
        for j in range(255):
            if color == "RAINBOW":
                light.rainbow_cycle(0.05,j)
            else:
                break
    baton.release()
def err():
    for i in range(2):
        light.fill((255,0,0))
        time.sleep(0.5)
        light.fill((0,0,0))
        time.sleep(0.5)
    light.fill(current)
def parseCom():
    global color
    global current
    global command
    if color.find("GRADIENT") != -1:
        col = color.split(" ")
        light.gradient(col[1],col[2])
        command = "GRADIENT" +" " + colors.Tuple(colors.readColor(col[1])) +" " + colors.Tuple(colors.readColor(col[2]))
        current = colors.readColor(col[1])

    elif color.find("SEND") !=-1:
        discord.send(discord.URL_POST_G,command)

    elif color.find("SAVE") !=-1:
        color = color.split(" ")
        if len(color) == 3:
            colors.write(color[2],current)
        if len(color) == 2:
            colors.write(color[1],current)

    elif color == ("RAINBOW"):
        command = color
        while(baton.locked()):
             time.sleep(0.1)
        _thread.start_new_thread(rainbow, ())

    elif color.find("MORSE") != -1:
        command = color
        while(baton.locked()):
             time.sleep(0.1)
        _thread.start_new_thread(morse, ())

    elif color.find("OFF") != -1:
        light.fade_out()

    elif color.find("ON") != -1:
        light.fade_in()
    else:
        col = colors.readColor(color)
        if col == 1:
            err()
        else:
            light.fill(col)
            command = colors.Tuple(col)
            current = col

color = discord.getData(discord.URL_GET_G)
color = color.upper()
print(color)
con = True
#light.fade_in()
parseCom()
discord.start()
while True:
    m = discord.getData(discord.URL_GET_G)
    if m == 'none':
        m = discord.getDataS()
    if m!='none' and m!=color:
        color = m

        color = color.upper()
        print(color)
        parseCom()

Set up:

Once you set everything up and turn it on, your lights will try to connect to the internet. If it can not do so in 10 seconds, it will blink red, and become a wifi access point that you can connect to with a phone or laptop. Connect to the light's wifi and search in a browser http://… You will be taken to the website that your light is broadcasting. There you can put in your wifi's SSID and password. The website will display other information like the mac address if you need it to connect to your network.

Full github repo: https://github.com/TheSeaUrchin/Friendship-Light

There is no blog available for this project :/