diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..13592fe --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +BUILD=`date +%FT%T%z` + +LDFLAGS=-ldflags "-X main.buildDate=${BUILD}" + +.PHONY: build deps static + +build: + go build ${LDFLAGS} + +deps: + go get + +static: + CGO_ENABLED=0 GOOS=linux go build ${LDFLAGS} -a -installsuffix cgo -o LEDController . \ No newline at end of file diff --git a/build/ci/drone.yml b/build/ci/drone.yml new file mode 100755 index 0000000..a2341ed --- /dev/null +++ b/build/ci/drone.yml @@ -0,0 +1,63 @@ +kind: pipeline +type: docker +name: linux-amd64-taggedver + +platform: + arch: amd64 + os: linux + +steps: +- name: build + image: golang + commands: + - pwd + - mkdir -p /go/src/git.martyn.berlin/martyn + - ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController + - cd /go/src/git.martyn.berlin/martyn/LEDController + - go get + - go build + +- name: publish + image: plugins/docker:18 + settings: + auto_tag: true + auto_tag_suffix: linux-amd64 + dockerfile: build/package/Dockerfile + repo: imartyn/karaokardbot + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - push + - tag + +trigger: + ref: + - refs/tags/v* + +--- +kind: pipeline +type: docker +name: linux-amd64-devel-master + +platform: + arch: amd64 + os: linux + +steps: +- name: build + image: golang + commands: + - pwd + - mkdir -p /go/src/git.martyn.berlin/martyn + - ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController + - cd /go/src/git.martyn.berlin/martyn/LEDController + - go get + - go build + +trigger: + ref: + - refs/heads/devel + - refs/heads/master \ No newline at end of file diff --git a/build/package/Dockerfile b/build/package/Dockerfile new file mode 100755 index 0000000..c267c18 --- /dev/null +++ b/build/package/Dockerfile @@ -0,0 +1,14 @@ +FROM golang@sha256:cee6f4b901543e8e3f20da3a4f7caac6ea643fd5a46201c3c2387183a332d989 AS builder +RUN apk update && apk add --no-cache git make ca-certificates && update-ca-certificates +COPY main.go /go/src/git.martyn.berlin/martyn/LEDController/ +COPY internal/ /go/src/git.martyn.berlin/martyn/LEDController/internal/ +COPY Makefile /go/src/git.martyn.berlin/martyn/LEDController/ +RUN cd /go/src/git.martyn.berlin/martyn/LEDController/; make deps ; make static + +FROM scratch +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /go/src/git.martyn.berlin/martyn/LEDController /app/ +COPY strings.json /app/strings.json +COPY web/ /app/web/ +WORKDIR /app +CMD ["/app/LEDController"] \ No newline at end of file diff --git a/internal/patterns/gradient.go b/internal/patterns/gradient.go index b1f0ed1..638a6b0 100755 --- a/internal/patterns/gradient.go +++ b/internal/patterns/gradient.go @@ -13,4 +13,19 @@ func Gradient(fromR byte, fromG byte, fromB byte, toR byte, toG byte, toB byte, ret[i+2] = fromB + byte(float32(i/3)*stepB) } return ret +} + +func GradientPanel(w int, h int, fromColour RGBcolor, toColour RGBcolor) [][]RGBcolor { + out := FillPanel(w, h, 255, 0, 0) + var stepR float32 = (float32(toColour[0]) - float32(fromColour[0])) / float32(w) + var stepG float32 = (float32(toColour[1]) - float32(fromColour[1])) / float32(w) + var stepB float32 = (float32(toColour[2]) - float32(fromColour[2])) / float32(w) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + out[x][y][0] = fromColour[0] + byte(float32(x)*stepR) + out[x][y][1] = fromColour[1] + byte(float32(x)*stepG) + out[x][y][2] = fromColour[2] + byte(float32(x)*stepB) + } + } + return out } \ No newline at end of file diff --git a/internal/patterns/plaincolour.go b/internal/patterns/plaincolour.go index d4b82da..0b2af52 100755 --- a/internal/patterns/plaincolour.go +++ b/internal/patterns/plaincolour.go @@ -9,6 +9,7 @@ func RedPanel(w int, h int) [][]RGBcolor { var lastColour RGBcolor var lastIteration uint16 = 0 +var lastPlasmaValue byte = 0 func RandomColourPanel(w int, h int, speed uint16) [][]RGBcolor { if lastIteration > 0 { @@ -24,4 +25,18 @@ func RandomColourPanel(w int, h int, speed uint16) [][]RGBcolor { lastIteration = speed } return FillPanel(w, h, lastColour[0], lastColour[1], lastColour[2]) +} + +func PlasmaColourPanel(w int, h int, speed uint16) [][]RGBcolor { + if lastIteration > 0 { + lastIteration -= 40 + } + if lastIteration <= 0 { + lastPlasmaValue++ + var newColour RGBcolor + newColour[0],newColour[1],newColour[2] = plasmaRGBFromVal(lastPlasmaValue) + lastColour = newColour + lastIteration = speed + } + return FillPanel(w, h, lastColour[0], lastColour[1], lastColour[2]) } \ No newline at end of file diff --git a/internal/queue/queue.go b/internal/queue/queue.go index 3a25e4a..28e4c35 100755 --- a/internal/queue/queue.go +++ b/internal/queue/queue.go @@ -1,7 +1,11 @@ package queue +type RGBcolor = [3]byte + type QueueItem struct { Effect string Duration uint16 Speed uint16 //only used by some patterns + SeedColour RGBcolor // only used by some patterns + SecondColour RGBcolor // only used by some patterns } \ No newline at end of file diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index 6ee788a..3badf27 100755 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -5,6 +5,7 @@ import ( "github.com/gorilla/handlers" "github.com/gorilla/mux" queue "git.martyn.berlin/martyn/LEDController/internal/queue" + "golang.org/x/image/colornames" "fmt" "net/http" @@ -35,13 +36,17 @@ func PatternHandler(response http.ResponseWriter, request *http.Request) { response.Header().Add("Content-type", "text/plain") var e queue.QueueItem e.Effect = vars["pattern"] - i, _ := strconv.Atoi(vars["duration"]) - e.Duration = uint16(i) - _, found := vars["speed"] + _, found := vars["duration"] + if found { + i, _ := strconv.Atoi(vars["duration"]) + e.Duration = uint16(i) + } else { + e.Duration = 5000 + } + _, found = vars["speed"] if found { i, _ := strconv.Atoi(vars["speed"]) e.Speed = uint16(i) - fmt.Printf("Speed passed %d\n",e.Speed) } else { e.Speed = 40 } @@ -49,6 +54,64 @@ func PatternHandler(response http.ResponseWriter, request *http.Request) { fmt.Fprint(response, "OKAY") } +func ColourHandler(response http.ResponseWriter, request *http.Request) { + vars := mux.Vars(request) + response.Header().Add("Content-type", "text/plain") + var e queue.QueueItem + e.Effect = "colour" + _, found := vars["duration"] + if found { + i, _ := strconv.Atoi(vars["duration"]) + e.Duration = uint16(i) + } else { + e.Duration = 5000 + } + var c queue.RGBcolor + for cn, cv := range(colornames.Map) { + if cn == vars["name"] { + c[0] = cv.R + c[1] = cv.G + c[2] = cv.B + } + } + e.SeedColour = c + globalQueue <- e + fmt.Fprint(response, "OKAY") +} + +func FadeHandler(response http.ResponseWriter, request *http.Request) { + vars := mux.Vars(request) + response.Header().Add("Content-type", "text/plain") + var e queue.QueueItem + e.Effect = "fade" + _, found := vars["duration"] + if found { + i, _ := strconv.Atoi(vars["duration"]) + e.Duration = uint16(i) + } else { + e.Duration = 5000 + } + var c queue.RGBcolor + for cn, cv := range(colornames.Map) { + if cn == vars["namefrom"] { + c[0] = cv.R + c[1] = cv.G + c[2] = cv.B + } + } + e.SeedColour = c + for cn, cv := range(colornames.Map) { + if cn == vars["nameto"] { + c[0] = cv.R + c[1] = cv.G + c[2] = cv.B + } + } + e.SecondColour = c + globalQueue <- e + fmt.Fprint(response, "OKAY") +} + func HandleHTTP(queueChannel chan queue.QueueItem) { globalQueue = queueChannel r := mux.NewRouter() @@ -56,8 +119,15 @@ func HandleHTTP(queueChannel chan queue.QueueItem) { r.NotFoundHandler = http.HandlerFunc(NotFoundHandler) r.HandleFunc("/", RootHandler) r.HandleFunc("/healthz", HealthHandler) + r.HandleFunc("/pattern/{pattern}", PatternHandler) r.HandleFunc("/pattern/{pattern}/{duration}", PatternHandler) r.HandleFunc("/pattern/{pattern}/{duration}/{speed}", PatternHandler) + r.HandleFunc("/colour/{name}", ColourHandler) + r.HandleFunc("/colour/{name}/{duration}", ColourHandler) + r.HandleFunc("/color/{name}", ColourHandler) + r.HandleFunc("/color/{name}/{duration}", ColourHandler) + r.HandleFunc("/fade/{namefrom}/{nameto}", FadeHandler) + r.HandleFunc("/fade/{namefrom}/{nameto}/{duration}", FadeHandler) http.Handle("/", r) srv := &http.Server{ Handler: loggedRouter, diff --git a/main.go b/main.go index 1213e67..a222062 100755 --- a/main.go +++ b/main.go @@ -33,6 +33,13 @@ func foreverLoop() { } } +func reduceBrightness(universe [512]byte, percentage int) [512]byte{ + for i := range(universe) { + universe[i] = byte(float64(universe[i]) * (float64(percentage) / float64(100))) + } + return universe +} + func main() { go func() { fmt.Printf("Starting webserver on port %s\n", "5353") @@ -99,13 +106,19 @@ func main() { var red patterns.RGBcolor red[0] = 255 rearranged = remapping.SliceRearrange(68, 4, true, remapping.XYGridToLinear(68, 4, patterns.SineChase(68, 4, black, red, currentEffect.Speed))) + case "plasmapulse": + rearranged = remapping.SliceRearrange(68, 4, false, remapping.XYGridToLinear(68, 4, patterns.PlasmaColourPanel(68,4, currentEffect.Speed))) + case "colour": + rearranged = remapping.SliceRearrange(68, 4, false, remapping.XYGridToLinear(68, 4, patterns.FillPanel(68,4, currentEffect.SeedColour[0], currentEffect.SeedColour[1], currentEffect.SeedColour[2]))) + case "fade": + rearranged = remapping.SliceRearrange(68, 4, true, remapping.XYGridToLinear(68, 4, patterns.GradientPanel(68,4, currentEffect.SeedColour, currentEffect.SecondColour))) default: rearranged = remapping.SliceRearrange(68, 4, false, remapping.XYGridToLinear(68, 4, patterns.FillPanel(68,4, 128,0,128))) } //rearranged := remapping.SliceRearrange(68,4,true,linearPlasma(68*4)) sema <- struct{}{} // acquire token - currentFrame[0] = remapping.Slice512(rearranged[0]) - currentFrame[1] = remapping.Slice512(rearranged[1]) + currentFrame[0] = reduceBrightness(remapping.Slice512(rearranged[0]),10) + currentFrame[1] = reduceBrightness(remapping.Slice512(rearranged[1]),10) <- sema time.Sleep(40 * time.Millisecond) }