Posted in raspberrypi, led

header_lq.jpg(photo made by Sfecles Petru)

Did you ever create a game in Raspberry Pi? 

For me, it's the first one, and it's a zero-player game called Game Of Life, also known as Conway Game. 

The game starts with a pattern (initial input) being the only interaction that the user/creator will have with the game. Next, you sit back and relax observing how the initial pattern evolves over time.

Let's see how it evolves! Feel free to use the comment section for any question, curiosity or problem.

The hardware to play with

As the title suggests, you need a Raspberry Pi, you can use any variant you want, for this project I will use a Rasp Zero.

The Game Of Life works in an infinite 2D space but for simplicity of this project, I chose to use an 8x8 LED matrix. But you are not limited, feel free to use as many and any size you like. 

Raspberry Pi and MAX7219 LED Matrix connections

Because I'm using a matrix with a MAX7219 (SPI) the following table shows how to create the connections. If you are using WS2812 NeoPixels (DMA)/NeoSegments please use the following instructions to make the connections.

 MAX7219Raspberry Pi Zero
1VCC (5V)VCC 5V (PIN 2)
2GNDGND (PIN 4)
3DINGPIO10 with MOSI (PIN 19)
4CSGPIO8 with SPI CE0 (PIN 24)
5CLKGPIO11 with SPI CLK (PIN 23)

The code evolves

The game's universe is governed by 4 well-defined laws. The universe is formed by a grid of cells/dots which can be dead or alive. For this project, the dead cells represent turned off LEDs and the live cells are represented by bright LEDs. On each iteration, the laws are applied and the shape changes. The laws:

  1. Any live cell with fewer than two live neighbors dies as if caused by underpopulation.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by overpopulation.
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

We need to enable SPI, an easy tutorial can be found on luma.led_matrix documentation. If you don't want to open the link, just run sudo raspi-config and enable SPI in Interfacing Options. Install lume.led_matrix library in python by running the following commands and we are ready to go:

sudo usermod -a -G spi,gpio pi
sudo apt-get install build-essential python-dev python-pip libfreetype6-dev libjpeg-dev
sudo -H pip install --upgrade luma.led_matrix

In the code, we start with the initial universe, we draw it to our matrix and then are applied the laws of the universe. After this, a new universe is born and the process starts with displaying it.

When I created the device, I encountered some problems (still a mystery) with the initialization. The only way to make it work was to return it from a function and I don't have any idea why it works.

I created 3 main functions, one that is brightening the LEDs from the matrix (drawMatrix), one that applies the laws of the universe (gameOfLife) and another one that counts the neighbors of a cell from the matrix (countNeighbors).

Feel free to modify the UNIVERSE_WIDTH and UNIVERSE_HEIGHT constants for your project.

With a few modification, you can use other devices, feel free to adapt the code for your use case. A good start is to replace getDevice function, if you are not using MAX7219 with the respective code that can be easily obtained from the documentation of luma.

import time

from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.render import canvas

UNIVERSE_WIDTH = 8
UNIVERSE_HEIGHT = 8

def isAlive(cell):
    return cell == 1

def drawMatrix(device, universe):
    with canvas(device) as draw:
        for y in range(UNIVERSE_WIDTH):
            for x in range(UNIVERSE_HEIGHT):
                fill = 'white' if isAlive(universe[x][y]) else 'black' 
                draw.point((x,y), fill=fill)

def countNeighbors(universe, cellX, cellY):
    count = 0
    neighborsRadius =[-1, 0, 1]
    for x in neighborsRadius:
        neighbordPositionX = cellX + x
        if neighbordPositionX >= UNIVERSE_WIDTH or neighbordPositionX < 0:
            continue

        for y in neighborsRadius:
            neighbordPositionY = cellY + y
            if neighbordPositionY >= UNIVERSE_HEIGHT or neighbordPositionY < 0:
                continue

            if cellX != neighbordPositionX or cellY != neighbordPositionY:
                count += universe[neighbordPositionX][neighbordPositionY]
    
    return count

def gameOfLife(universe):
    # copy the universe so that we can modify it without worry 
    newUniverse = [row[:] for row in universe] 
    for cellX in range(UNIVERSE_WIDTH):
        for cellY in range(UNIVERSE_HEIGHT):
            neighbors = countNeighbors(universe, cellX, cellY)

            if isAlive(newUniverse[cellX][cellY]):
                # Any live cell with fewer than two live neighbors dies, as if by under population.
                # Any live cell with more than three live neighbors dies, as if by overpopulation.
                if neighbors < 2 or neighbors > 3:
                    newUniverse[cellX][cellY] = 0
                # Any live cell with two or three live neighbors lives on to the next generation.
                elif neighbors == 2 or neighbors == 3:
                    newUniverse[cellX][cellY] = 1
            # Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
            elif neighbors == 3:
                newUniverse[cellX][cellY] = 1
    
    return newUniverse


def getDevice(n, block_orientation, rotate):
    # create matrix device
    serial = spi(port=0, device=0, gpio=noop())
    device = max7219(serial, cascaded=n or 1, block_orientation=block_orientation, rotate=rotate or 0)
    return device

if __name__ == '__main__':
    try:   

        device = getDevice(1, 0, 0)

        # 1/4 pulsar, feel free to replace/add different life forms
        universe = [
            [0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0, 1, 1, 0],
            [0, 0, 0, 0, 0, 0, 0, 0],
            [0, 1, 1, 1, 0, 0, 1, 1],
            [0, 0, 0, 1, 0, 1, 0, 1],
            [0, 0, 0, 0, 0, 1, 1, 0]
        ]

        while 1:
            drawMatrix(device, universe)
            time.sleep(1)
            universe = gameOfLife(universe)
 
        # close execution by pressing CTRL + C
    except KeyboardInterrupt:
        print("Intrerrupted by user")
        pass
    finally:
        print("Program stopped")

Run the code above will bright the LEDs for a quarter of a pulsar. I saved the above code in a led-matrix.py file, run it using the command below and press CTRL + C to cancel anytime.

python led-matrix.py

A quarter of a pulsar

Conclusion

Besides the obvious extension that can be made, adding more matrixes, another one is to make the 'universe round'. When the bounds are crossed, the game to continue from the opposite side of the border.

This game can be moved to different sources of light, disco ball, a room full of lights and many other to make custom patterns from an initial input, maybe by touching.

Thank you for reading and stay bright.

Posted in raspberrypi, led

How to make a linear color transition with an RGB LED(photo made by Sfecles Petru)

Why another post about RGB LEDs when are thousands already? I searched everywhere, but I couldn't find something about color transitions for an RGB LED.

Usually, when changing the color of an RGB LED, everything is happening in an instant (e.g., RED to BLUE). Being unpleasant for the eye may be irritating. Depending on the situations, you need a beautiful and calm change in color that is more appealing to the eye (why not, to the brain ultimately).

The pin connections.

I use a Raspberry Pi Zero and an RGB LED connected to PWM. Don't be afraid to use any other Rasp version, or to adapt to any other board type you are using (e.g., Arduino).

RGB LED connected to Raspberry Pi Zero

The common node for the RGB LED I'm using it is the anode. I connected it to the 5V pin and the other pins to PWM. If the common node for your RGB LED is the cathode, attach it to GND.

In the below table are the pin associations that I made.

RGB LEDSignalRaspberry Pi Zero
1REDGPIO 12 (PWM)
2GREENGPIO 13 (PWM)
3BLUEGPIO 19 (PWM)
4Anode 2 (5V VCC)

The code.

During a transition I have multiple intermediary colors between the current color and the end color: [currentColor, color1, color2, ... , colorN, targetColor]. I'm using the color HEX codes to represent the colors. It can be replaced very easily with RGB color model if that fits your project better.

The number of intermediary colors is given by the duration of a transition and the number of frames per second (fps). As an example, for a duration of 2s and 30 fps, it will result in a ~60 intermediary colors (steps).

Each step will increment/decrement the current color in order to reach the desired color. This is very similar to a linear function and we can say that this is a linear color transition. 

The first thing is to find the distance from the current color to the new color. The distance is computed for each color from the LED (Red, Green, and Blue). The distance is used to compute the incrementing value at each step. The incrementing value is applied individually per color. This way the LED will make a linear transition to the new color.

In order to control the each LED intensity, I transform the hex color value in percent. This way, it can be easily passed to the duty cycle function. 

The RGB LEDs that I have work with a maximum frequency of 100Hz. 

The code works for both type of RGB LEDs with common node cathode or anode. By default is using the cathode, but you can change this by modifying the constant value COMMON_NODE to 0.

I added a list of colors that demonstrate the behavior, you can change this with the colors that you want.

import RPi.GPIO as GPIO
import time

# use channel numbers on the Broadcom SOC
GPIO.setmode(GPIO.BCM)

# defining the pins
GPIO_RED = 12
GPIO_GREEN = 13
GPIO_BLUE = 19

# defining the pins as output
GPIO.setup(GPIO_RED, GPIO.OUT)
GPIO.setup(GPIO_GREEN, GPIO.OUT)
GPIO.setup(GPIO_BLUE, GPIO.OUT)

# choosing a frequency for pwm
PWM_FREQUENCY = 50

# configure to use PWM
COLOR_PWM = [ 
    GPIO.PWM(GPIO_RED, PWM_FREQUENCY),
    GPIO.PWM(GPIO_GREEN, PWM_FREQUENCY),
    GPIO.PWM(GPIO_BLUE, PWM_FREQUENCY)
]

# For CATHODE this must be 0
# For ANODE this must be 1
COMMON_NODE = 1

def transition(currentColor, targetColor, duration, fps):
    distance = colorDistance(currentColor, targetColor)
    increment = calculateIncrement(distance, fps, duration)

    for i in range(0, int(fps)):
        transitionStep(currentColor, targetColor, increment)
        time.sleep(duration/fps)

def colorDistance(currentColor, targetColor):
    distance = [0, 0, 0]

    for i in range(len(currentColor)):
        distance[i] = abs(currentColor[i] - targetColor[i])

    return distance

def calculateIncrement(distance, fps, duration):
    increment = [0, 0, 0]

    for i in range(len(distance)):
        inc = abs(distance[i] / fps)
        increment[i] = inc
    return increment

def transitionStep(currentColor, targetColor, increment):
    for i in range(len(currentColor)):
        if currentColor[i] > targetColor[i]:
            currentColor[i] -= increment[i]
            if currentColor[i] <= targetColor[i]:
                increment[i] = 0
        else:
            currentColor[i] += increment[i]
            if currentColor[i] >= targetColor[i]:
                increment[i] = 0
    setColor(currentColor)

def setColor(color):
    for i in range(len(COLOR_PWM)):
        percent = hexPercent(color[i])
        if COMMON_NODE:
            percent = 100 - percent
        COLOR_PWM[i].ChangeDutyCycle(percent)

def hexPercent(color):
    percent = (color / float(0xFF)) * 100
    return percent

if __name__ == '__main__':
    try:
        for i in range(len(COLOR_PWM)):
            COLOR_PWM[i].start(1)

        duration = 2.0
        fps = 90.0

        i = 0
        while 1:
            colors = [
                [0xE8, 0x0B, 0x0B], # red
                [0x0b, 0x12, 0xe8], # blue
                [0xe8, 0xd5, 0x0b], # yellow
                [0xdd, 0x0b, 0xe8], # purple
                [0x1A, 0xe8, 0x0B], # green
                [0x0b, 0xd5, 0xe8]  # teal
            ]

            currentColor = colors[i % len(colors)]

            i = (i + 1) % len(colors)
            nextColor = colors[i]

            transition(currentColor, nextColor, duration, fps)

        # close execution by pressing CTRL + C
    except KeyboardInterrupt:
        print("Intrerrupted by user")
        pass
    finally:
        print("Program stopped")
        for colorPwm in COLOR_PWM:
            colorPwm.stop()
        GPIO.cleanup()

Run the code using the following command on the terminal and press CTRL + C to cancel anytime.

python rgb_transition.py

Conclusions

Linear color transition from red to blue

In the above gif, I made the led to make a transition from RED to BLUE. I covered with a square opaque glass in order to highlight the transition.

This project can be expanded to work with a matrix/band of RGB LEDs and to create other types of transition. Feel free to share in the comments what you achieved.

Always aim for the best and never give up.