Za naš prvi projekat odlučili smo da povežemo 8×8 WS2812 LED panel sa Raspberry Pi Pico-m. Zbog ugodnosti rada i inicijalnog testiranje koristili smo picoBricks ploču koja nam je zgodna jer na sebi ima izvučene pinove i jedan broj već povezanih periferija.Naravno, za sam projekat ništa od tih periferija nije bilo potrebno, sem WS2812 modula koji nam je služio dok smo čekali 8×8 matriks.

No kada je matrix stigao, jednostavno smo povezali DIN na GPIO8 i VCC i GND na pripadajuće pinove.

TinyGo WS2812 Raspberry Pi PicoW

Iako na modulu stoji 5V, pokazalo se da je 3,3 sasvim dovoljno za jedan modul i bez bojazni to i uradite. Ostale konektore ostavite slobodnim.

import (
"fmt"
"image/color"
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/ws2812"
)

var neo = machine.GPIO8
var leds [64]color.RGBA

var colorString, numberHEX string
var rgbOUTA color.RGBA

Pored standardnih biblioteka, treba dodati i drajver za ws2812. Ono što nam se dopada je podrška koju je razvojni tim TinyGo-a uložio u pisanje drajvera za najčešće početne I/O jedinice kao i za pisanje primera za njihovo korišćenje.

Treba primetiti i image/colour biblioteku i interesantan color.RGBA. RGBA je struktura koja se sastoji od 4 uint8 promenjive sa vrednošću R, G, B i Alpha za svaku tačku, odnosno diodu u matriksu. Obratite pažnju da ws2812 nema mogućnost korišćenja Alpha vrednosti i da se intenzitet kod ovih višekolornih dioda radi PWM kontrolom napajanja.

func showOnLed(drawingHEX []int) {
   neo.Configure(machine.PinConfig{Mode: machine.PinOutput})

   ws := ws2812.New(neo)

   for i := 0; i <= 63; i++ {
      var s = int64(drawingHEX[i])
      numberHEX = strconv.FormatInt(int64(s), 16)
      numberHEX = fmt.Sprintf("%06s", numberHEX)

      first2 = numberHEX[0 : len(numberHEX)-4]
      middle2 = numberHEX[len(numberHEX)-4 : len(numberHEX)-2]
      last2 = numberHEX[len(numberHEX)-2:]

      R, _ := strconv.ParseUint(first2, 16, 32)
      rgbOUTA.R = uint8(R)

      G, _ := strconv.ParseUint(middle2, 16, 32)
      rgbOUTA.G = uint8(G)

      B, _ := strconv.ParseUint(last2, 16, 32)
      rgbOUTA.B = uint8(B)

      leds[i] = color.RGBA{R: rgbOUTA.R, G: rgbOUTA.G, B: rgbOUTA.B}

   }
   ws.WriteColors(leds[:])

}

showOnLed je funkcija koju smo napisali i njena osnovna namena je da hexa broj oblika 0xrrggbb pretvori i vrati u obliku tri decimalna broja sa vrednostima R, G i B koje šaljemo matriksu. Veoma jednostavna funkcija.

Pretvaranje 8×8 pix slike u 0xrrggbb je najbolje uraditi pomoću lcd-image-converter programa koji je javno dostupan na netu. Startuje se bez instaliranja i jedino na šta morate obratiti pažnju je redosled kojim se pali matriks. U našem slučaju kreće od gore desno, pa redom nalevo do kraja, pa se spušta red ispod i kreće nadesno… i tako u cik cak. Obratite pažnju pri generisanju oxrrggbb niza da redosled bude odgovarajući da bi slike na matriksu bile kako treba.

JetBrains GoLand TinyGo IDE

U našem primeru smo uradili šest slika koje su, neke manje, neke više uspele. Trebalo je sve prebaciti u 8×8 matriks.

CreeperHEX

creeperHEX := []int{0x09660e, 0x09660e, 0x055f0a, 0x005202, 0x0a6a10, 0x299a2e, 0x0a690f, 0x09660e,
      0x0a670f, 0x035307, 0x065a0a, 0x0d6b12, 0x1b8220, 0x065709, 0x085c0d, 0x0e6c13,
      0x08610d, 0x000000, 0x000e00, 0x075a0c, 0x0a640f, 0x000000, 0x000200, 0x17771b,
      0x004500, 0x000000, 0x000000, 0x003f00, 0x004400, 0x000000, 0x000000, 0x0f6a14,
      0x004400, 0x003500, 0x001c00, 0x000000, 0x000000, 0x126415, 0x075c0c, 0x0a670f,
      0x09660e, 0x075a0a, 0x000000, 0x000000, 0x000000, 0x000000, 0x004400, 0x06600a,
      0x27982b, 0x0d6310, 0x001d00, 0x0a5c0e, 0x1c7e20, 0x000000, 0x0e6e13, 0x004c00,
      0x0e6e13, 0x0c6910, 0x0c640f, 0x0d6b12, 0x15781a, 0x0c640f, 0x0a670f, 0x07610c}

TinyGo WS2812 Raspberry Pi PicoW

Uradili smo ga prvog i shvatili da nam je potreban difuzor. Na redakcijskom Formlabs Form3+ smo napravili jedan.

Screen

MehatronikaHEX

mehatronikaHEX := []int{0x15413c, 0x103a34, 0x0c3730, 0x0c3630, 0x0d3731, 0x0d3c36, 0x12463f, 0x13433d,
      0x2d5d58, 0xbed4d3, 0x09443c, 0x054339, 0x043b33, 0x09443c, 0xc7deda, 0x2a625b,
      0x2d605a, 0xa6cac7, 0x8fc0be, 0x074a41, 0x06423a, 0x8ebab7, 0x95c4c2, 0x2a625b,
      0x2d615b, 0x4f978d, 0x70aea7, 0x62a39d, 0x66a39d, 0x6da39d, 0x478980, 0x2c645d,
      0x275e57, 0x38887f, 0x074f45, 0x89bab6, 0x8bbbb7, 0x084940, 0x3b7e75, 0x266059,
      0x0d413b, 0x024238, 0x0e554a, 0x084d43, 0x084c43, 0x06473c, 0x08443c, 0x0d433c,
      0x204f49, 0x80a9a5, 0x4c8a84, 0x679d95, 0x7aaba9, 0x72a5a1, 0x5e9890, 0x1d544c,
      0x123d37, 0x0d3730, 0x0d3832, 0x0d3a33, 0x0f3e37, 0x0e3f37, 0x0f4038, 0x133e38}

Mehatronika logo na TinyGo WS2812 Raspberry Pi PicoW

Treba gledati iz daljine i deluje ok prepoznatljivo.

loveHEX := []int{0x010000, 0x1a0000, 0x230000, 0x0d0000, 0x0d0000, 0x230000, 0x1a0000, 0x010000,
      0x340000, 0xd70101, 0xf70101, 0xa30101, 0xa30101, 0xf70101, 0xd70101, 0x340000,
      0x780000, 0xff0101, 0xff0101, 0xfc0101, 0xfc0101, 0xff0101, 0xff0101, 0x780000,
      0x630000, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0x630000,
      0x160000, 0xca0101, 0xfb0101, 0xff0101, 0xff0101, 0xfb0101, 0xca0101, 0x160000,
      0x000000, 0x370000, 0xbd0101, 0xf90101, 0xf90101, 0xbd0101, 0x370000, 0x000000,
      0x000000, 0x000000, 0x2b0000, 0xb00101, 0xaf0101, 0x2b0000, 0x000000, 0x000000,
      0x000000, 0x000000, 0x000000, 0x100000, 0x100000, 0x000000, 0x000000, 0x000000}

Pa onda loveHEX:

Pa jedan interesantni plavi Sonic

blueHEX := []int{0x00affe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00affe, 0x00affe,
      0x00affe, 0x00a4fb, 0x0025e7, 0x003afe, 0x0025e7, 0x002bfe, 0x002bfe, 0x009dfe,
      0x002bfe, 0x003afe, 0x0025e7, 0x000000, 0xf5efea, 0x000000, 0x009de6, 0x00affe,
      0x00affe, 0x00a4fb, 0xf59d65, 0xf59d65, 0xf59d65, 0x0033f4, 0x009de6, 0x003afe,
      0x00a4fb, 0x002fff, 0x002fff, 0x002ffa, 0x002ffa, 0x002ffa, 0x002fff, 0x00a4fb,
      0x00a4fb, 0x0025e7, 0x002ffa, 0xe8986b, 0xe8986b, 0x002ffa, 0x0025e7, 0x00a4fb,
      0x00affe, 0xf5efea, 0x002ffa, 0xffa160, 0xffa160, 0x002ffa, 0xf5efea, 0x00affe,
      0x00affe, 0x009de6, 0xd90000, 0x009de6, 0x009de6, 0xd90000, 0x009de6, 0x00affe}

Još jedno srce, drugog vajba.

heartHEX := []int{0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a,
      0xef9a9a, 0x000000, 0x000000, 0xef9a9a, 0xef9a9a, 0x000000, 0x000000, 0xef9a9a,
      0x000000, 0xffffff, 0xffffff, 0x000000, 0x000000, 0xb71c1c, 0xb71c1c, 0x000000,
      0x000000, 0xb71c1c, 0xb71c1c, 0xd50000, 0xd50000, 0xd50000, 0xffffff, 0x000000,
      0x000000, 0xd50000, 0xd50000, 0xd50000, 0xb71c1c, 0xb71c1c, 0xb71c1c, 0x000000,
      0xef9a9a, 0x000000, 0xb71c1c, 0xb71c1c, 0xb71c1c, 0xd50000, 0x000000, 0xef9a9a,
      0xef9a9a, 0xef9a9a, 0x000000, 0xb71c1c, 0xb71c1c, 0x000000, 0xef9a9a, 0xef9a9a,
      0xef9a9a, 0xef9a9a, 0xef9a9a, 0x000000, 0x000000, 0xef9a9a, 0xef9a9a, 0xef9a9a}

I najzad neki Pikačuoliki

pikachuHEX := []int{0x000000, 0x35ed11, 0x35ed11, 0x35ed11, 0x35ed11, 0x000000, 0x000000, 0x35ed11,
      0x35ed11, 0x35ed11, 0xffff00, 0xffea00, 0x35ed11, 0x35ed11, 0x35ed11, 0xffea00,
      0xffea00, 0xffff00, 0xffff00, 0xffff00, 0xffff00, 0x35ed11, 0x35ed11, 0x35ed11,
      0xffea00, 0xffea00, 0x35ed11, 0xffff00, 0x000000, 0xffff00, 0xffff00, 0x000000,
      0xffea00, 0xffff00, 0xffff00, 0xffff00, 0xd50000, 0x35ed11, 0xffea00, 0xffea00,
      0x35ed11, 0x4e342e, 0x35ed11, 0xffff00, 0xffea00, 0xffea00, 0xffea00, 0x35ed11,
      0x35ed11, 0xffff00, 0xffea00, 0xffff00, 0xffea00, 0xffff00, 0x4e342e, 0x35ed11,
      0x35ed11, 0x35ed11, 0xffff00, 0xffea00, 0x4e342e, 0x4e342e, 0xffea00, 0x35ed11}

I sada, puštamo sve u krug kao slajdove.

// WS2812 RGB LED 8x8
// on rPi PicoW
// in JetBrains GoLand 2023.1
// by magazinMehatronika 2023

package main

import (
   "fmt"
   "image/color"
   "machine"
   "strconv"
   "time"
   "tinygo.org/x/drivers/ws2812"
)

var neo = machine.GPIO8
var leds [64]color.RGBA

var colorString, numberHEX string
var rgbOUTA color.RGBA

var first2, middle2, last2 string

func showOnLed(drawingHEX []int) {
   neo.Configure(machine.PinConfig{Mode: machine.PinOutput})

   ws := ws2812.New(neo)

   for i := 0; i <= 63; i++ {
      var s = int64(drawingHEX[i])
      numberHEX = strconv.FormatInt(int64(s), 16)
      numberHEX = fmt.Sprintf("%06s", numberHEX)

      first2 = numberHEX[0 : len(numberHEX)-4]
      middle2 = numberHEX[len(numberHEX)-4 : len(numberHEX)-2]
      last2 = numberHEX[len(numberHEX)-2:]

      R, _ := strconv.ParseUint(first2, 16, 32)
      rgbOUTA.R = uint8(R)

      G, _ := strconv.ParseUint(middle2, 16, 32)
      rgbOUTA.G = uint8(G)

      B, _ := strconv.ParseUint(last2, 16, 32)
      rgbOUTA.B = uint8(B)

      leds[i] = color.RGBA{R: rgbOUTA.R, G: rgbOUTA.G, B: rgbOUTA.B}

   }
   ws.WriteColors(leds[:])

}

func main() {

   mehatronikaHEX := []int{0x15413c, 0x103a34, 0x0c3730, 0x0c3630, 0x0d3731, 0x0d3c36, 0x12463f, 0x13433d,
      0x2d5d58, 0xbed4d3, 0x09443c, 0x054339, 0x043b33, 0x09443c, 0xc7deda, 0x2a625b,
      0x2d605a, 0xa6cac7, 0x8fc0be, 0x074a41, 0x06423a, 0x8ebab7, 0x95c4c2, 0x2a625b,
      0x2d615b, 0x4f978d, 0x70aea7, 0x62a39d, 0x66a39d, 0x6da39d, 0x478980, 0x2c645d,
      0x275e57, 0x38887f, 0x074f45, 0x89bab6, 0x8bbbb7, 0x084940, 0x3b7e75, 0x266059,
      0x0d413b, 0x024238, 0x0e554a, 0x084d43, 0x084c43, 0x06473c, 0x08443c, 0x0d433c,
      0x204f49, 0x80a9a5, 0x4c8a84, 0x679d95, 0x7aaba9, 0x72a5a1, 0x5e9890, 0x1d544c,
      0x123d37, 0x0d3730, 0x0d3832, 0x0d3a33, 0x0f3e37, 0x0e3f37, 0x0f4038, 0x133e38}

   loveHEX := []int{0x010000, 0x1a0000, 0x230000, 0x0d0000, 0x0d0000, 0x230000, 0x1a0000, 0x010000,
      0x340000, 0xd70101, 0xf70101, 0xa30101, 0xa30101, 0xf70101, 0xd70101, 0x340000,
      0x780000, 0xff0101, 0xff0101, 0xfc0101, 0xfc0101, 0xff0101, 0xff0101, 0x780000,
      0x630000, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0xff0101, 0x630000,
      0x160000, 0xca0101, 0xfb0101, 0xff0101, 0xff0101, 0xfb0101, 0xca0101, 0x160000,
      0x000000, 0x370000, 0xbd0101, 0xf90101, 0xf90101, 0xbd0101, 0x370000, 0x000000,
      0x000000, 0x000000, 0x2b0000, 0xb00101, 0xaf0101, 0x2b0000, 0x000000, 0x000000,
      0x000000, 0x000000, 0x000000, 0x100000, 0x100000, 0x000000, 0x000000, 0x000000}

   blueHEX := []int{0x00affe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00a7fe, 0x00affe, 0x00affe,
      0x00affe, 0x00a4fb, 0x0025e7, 0x003afe, 0x0025e7, 0x002bfe, 0x002bfe, 0x009dfe,
      0x002bfe, 0x003afe, 0x0025e7, 0x000000, 0xf5efea, 0x000000, 0x009de6, 0x00affe,
      0x00affe, 0x00a4fb, 0xf59d65, 0xf59d65, 0xf59d65, 0x0033f4, 0x009de6, 0x003afe,
      0x00a4fb, 0x002fff, 0x002fff, 0x002ffa, 0x002ffa, 0x002ffa, 0x002fff, 0x00a4fb,
      0x00a4fb, 0x0025e7, 0x002ffa, 0xe8986b, 0xe8986b, 0x002ffa, 0x0025e7, 0x00a4fb,
      0x00affe, 0xf5efea, 0x002ffa, 0xffa160, 0xffa160, 0x002ffa, 0xf5efea, 0x00affe,
      0x00affe, 0x009de6, 0xd90000, 0x009de6, 0x009de6, 0xd90000, 0x009de6, 0x00affe}

   creeterHEX := []int{0x09660e, 0x09660e, 0x055f0a, 0x005202, 0x0a6a10, 0x299a2e, 0x0a690f, 0x09660e,
      0x0a670f, 0x035307, 0x065a0a, 0x0d6b12, 0x1b8220, 0x065709, 0x085c0d, 0x0e6c13,
      0x08610d, 0x000000, 0x000e00, 0x075a0c, 0x0a640f, 0x000000, 0x000200, 0x17771b,
      0x004500, 0x000000, 0x000000, 0x003f00, 0x004400, 0x000000, 0x000000, 0x0f6a14,
      0x004400, 0x003500, 0x001c00, 0x000000, 0x000000, 0x126415, 0x075c0c, 0x0a670f,
      0x09660e, 0x075a0a, 0x000000, 0x000000, 0x000000, 0x000000, 0x004400, 0x06600a,
      0x27982b, 0x0d6310, 0x001d00, 0x0a5c0e, 0x1c7e20, 0x000000, 0x0e6e13, 0x004c00,
      0x0e6e13, 0x0c6910, 0x0c640f, 0x0d6b12, 0x15781a, 0x0c640f, 0x0a670f, 0x07610c}

   heartHEX := []int{0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a, 0xef9a9a,
      0xef9a9a, 0x000000, 0x000000, 0xef9a9a, 0xef9a9a, 0x000000, 0x000000, 0xef9a9a,
      0x000000, 0xffffff, 0xffffff, 0x000000, 0x000000, 0xb71c1c, 0xb71c1c, 0x000000,
      0x000000, 0xb71c1c, 0xb71c1c, 0xd50000, 0xd50000, 0xd50000, 0xffffff, 0x000000,
      0x000000, 0xd50000, 0xd50000, 0xd50000, 0xb71c1c, 0xb71c1c, 0xb71c1c, 0x000000,
      0xef9a9a, 0x000000, 0xb71c1c, 0xb71c1c, 0xb71c1c, 0xd50000, 0x000000, 0xef9a9a,
      0xef9a9a, 0xef9a9a, 0x000000, 0xb71c1c, 0xb71c1c, 0x000000, 0xef9a9a, 0xef9a9a,
      0xef9a9a, 0xef9a9a, 0xef9a9a, 0x000000, 0x000000, 0xef9a9a, 0xef9a9a, 0xef9a9a}

   pikachuHEX := []int{0x000000, 0x35ed11, 0x35ed11, 0x35ed11, 0x35ed11, 0x000000, 0x000000, 0x35ed11,
      0x35ed11, 0x35ed11, 0xffff00, 0xffea00, 0x35ed11, 0x35ed11, 0x35ed11, 0xffea00,
      0xffea00, 0xffff00, 0xffff00, 0xffff00, 0xffff00, 0x35ed11, 0x35ed11, 0x35ed11,
      0xffea00, 0xffea00, 0x35ed11, 0xffff00, 0x000000, 0xffff00, 0xffff00, 0x000000,
      0xffea00, 0xffff00, 0xffff00, 0xffff00, 0xd50000, 0x35ed11, 0xffea00, 0xffea00,
      0x35ed11, 0x4e342e, 0x35ed11, 0xffff00, 0xffea00, 0xffea00, 0xffea00, 0x35ed11,
      0x35ed11, 0xffff00, 0xffea00, 0xffff00, 0xffea00, 0xffff00, 0x4e342e, 0x35ed11,
      0x35ed11, 0x35ed11, 0xffff00, 0xffea00, 0x4e342e, 0x4e342e, 0xffea00, 0x35ed11}

   for {

      showOnLed(blueHEX)
      time.Sleep(1500 * time.Millisecond)
      showOnLed(loveHEX)
      time.Sleep(1500 * time.Millisecond)
      showOnLed(mehatronikaHEX)
      time.Sleep(1500 * time.Millisecond)
      showOnLed(creeterHEX)
      time.Sleep(1500 * time.Millisecond)
      showOnLed(heartHEX)
      time.Sleep(1500 * time.Millisecond)
      showOnLed(pikachuHEX)
      time.Sleep(1500 * time.Millisecond)
   }
}

Zaključak

Koristili smo sasvim standardne TinyGo biblioteke („fmt“, „image/color“, „strconv“, „time“, „machine“) uz drajver za WS2812 („tinygo.org/x/drivers/ws2812“). Bilo je tu porođajnih muka, priznajemo. Naš prvi Go program priuštio nam je borbe sa sintaksom, konceptima i mogućnostima jezika. Ali, bilo je jako interesantno, vizuelno intrigantno i šareno.

Bez razočarenja da ne radi ili da je nešto nemoguće. Naučili smo mnogo o tome kako se slika pretvara u 8×8 matriks boja i koliko je važno udaljiti se da bi se dozvolilo mešanje svetlosti.

Mnogo novog i neobičnog, ali moramo reći interesantnog. Probajte i sami i sigurni smo da ćete poželeti ovakav 8×8 matriks u vašem prostoru sa vašim kreacijama.

 

 

Aleksandar Dakić
Follow me