Text Scroller

Text Scroller is a small project that shows how to use a LED stripe WS2812B with a STM32F3 Discovery and using Zephyr RTOS.
C
#include <errno.h>
#include <string.h>
#include <math.h>
#include "colors.h"
#define LOG_LEVEL 4
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);

#include <zephyr/kernel.h>
#include <zephyr/drivers/led_strip.h>
#include <zephyr/device.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/util.h>
#include <zephyr/random/rand32.h>

#define STRIP_NODE              DT_ALIAS(led_strip)
#define STRIP_NUM_PIXELS        DT_PROP(DT_ALIAS(led_strip), chain_length)

#define BT_DEVICE_NODE          DT_ALIAS(usart1)
#define PC_CONSOLE_NODE         DT_ALIAS(usart2)

#define DELAY_TIME K_MSEC(100)

//#define RGB(_r, _g, _b) { .r = (_r), .g = (_g), .b = (_b) }
/*
static const struct led_rgb colors[] = {
        RGB(0x0f, 0x00, 0x00), 
        RGB(0x00, 0x0f, 0x00), 
        RGB(0x00, 0x00, 0x0f), 
};
*/
static const struct led_rgb black = RGB(0x00, 0x00, 0x00);

struct led_rgb pixels[STRIP_NUM_PIXELS];

static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE);
static const struct device *const bt_uart_dev = DEVICE_DT_GET(BT_UART_DEVICE_NODE);
static const struct device *const pc_uart_dev = DEVICE_DT_GET(PC_UART_CONSOLE_NODE);

unsigned int letter, position;

#define MSG_SIZE 32
/* queue to store up to 10 messages (aligned to 4-byte boundary) */
K_MSGQ_DEFINE(uart_msgq, MSG_SIZE, 10, 4);

/* receive buffer used in UART ISR callback */
static char rx_buf[MSG_SIZE];
static int rx_buf_pos;

int scroll_text_length;
int current_char;

#define MAXLETTER 70
#define NRFONTCHR 43
#define SCREENDELAY 500
#define INITIALSPEED 30
#define COLUMNS 7
#define ROWS 8

// boolean
bool speedupSW, slowdownSW, stopScrolling;
bool scroll_to_left = true;
char scrollText[MAXLETTER];

#define CHRWIDTH 8
#define SCREENWIDTH 7
const char font_chars[NRFONTCHR]={'0','1','2','3','4','5','6','7','8','9',
                                 '`','~','.',' ','-',':','/',
                                 'a','b','c','d','e','f','g','h','i','j','k','l','m',
                                 'n','o','p','q','r','s','t','u','v','w','x','y','z'};

//const int charsMatrix[NRLETTERS][CHRWIDTH]={
const int font_bitmap8[NRFONTCHR][8]={
{0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C}, // 0
{0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x7E}, // 1
{0x3C, 0x66, 0x46, 0x06, 0x0C, 0x18, 0x30, 0x7E}, // 2
{0x7E, 0x06, 0x0C, 0x18, 0x0C, 0x06, 0x06, 0x7C}, // 3
{0x06, 0x0E, 0x16, 0x26, 0x7E, 0x06, 0x06, 0x06}, // 4
{0x7E, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C}, // 5
{0x3C, 0x66, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x3C}, // 6
{0x7E, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x18}, // 7
{0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C}, // 8
{0x3C, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x66, 0x3C}, // 9
{0x00, 0x24, 0x5A, 0x99, 0x81, 0x42, 0x24, 0x18}, // `heart
{0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C}, // ~smile
{0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00}, // .
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // space
{0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00}, // -
{0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00}, // :
{0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0}, // /
{0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F, 0x00}, // a
{0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E, 0x00}, // b
{0x00, 0x00, 0x0E, 0x10, 0x10, 0x11, 0x0E, 0x00}, // c
{0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x0F, 0x00}, // d
{0x00, 0x00, 0x0E, 0x11, 0x1E, 0x10, 0x0E, 0x00}, // e
{0x06, 0x09, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00}, // f
{0x00, 0x0f, 0x11, 0x11, 0x0F, 0x01, 0x0E, 0x00}, // g
{0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11, 0x00}, // h
{0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x0E, 0x00}, // i
{0x02, 0x00, 0x06, 0x02, 0x02, 0x12, 0x0C, 0x00}, // j
{0x10, 0x10, 0x12, 0x14, 0x18, 0x14, 0x12, 0x00}, // k
{0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x00}, // l
{0x00, 0x00, 0x1A, 0x15, 0x15, 0x11, 0x11, 0x00}, // m
{0x00, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11, 0x00}, // n
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E, 0x00}, // o
{0x00, 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10, 0x00}, // p
{0x00, 0x00, 0x0D, 0x13, 0x0F, 0x01, 0x01, 0x00}, // q
{0x00, 0x00, 0x16, 0x19, 0x10, 0x10, 0x10, 0x00}, // r
{0x00, 0x00, 0x0E, 0x10, 0x0E, 0x01, 0x1E, 0x00}, // s
{0x08, 0x08, 0x1C, 0x08, 0x08, 0x09, 0x06, 0x00}, // t
{0x00, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D, 0x00}, // u
{0x00, 0x00, 0x11, 0x11, 0x11, 0x0A, 0x04, 0x00}, // v
{0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A, 0x00}, // w
{0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00}, // x
{0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E, 0x00}, // y
{0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F, 0x00}  // z
};
void set_pixel(int x, int y, const struct led_rgb color)
{
    int cursor;
    if ((y % 2) == 0)
      cursor = x + (y * SCREENWIDTH);
    else
      cursor = (6 - x) + (y * SCREENWIDTH);
    memcpy(&pixels[cursor], &color, sizeof(struct led_rgb));
}

struct led_rgb get_pixel(int x, int y)
{
    int cursor;
    if ((y % 2) == 0)
      cursor = x + (y * SCREENWIDTH);
    else
      cursor = (6 - x) + (y * SCREENWIDTH);
    return pixels[cursor];
}

void scrollMatrix(bool scroll_to_left)
{
    int cursor_to, cursor_from;
    if (scroll_to_left)
      for (int x = 0; x < COLUMNS - 1; x++) 
        for (int y = 0; y < ROWS; y++) {
          if ((y % 2) == 0){
            cursor_to = x + (y * SCREENWIDTH);
            cursor_from = (x + 1) + y * SCREENWIDTH;
          }
          else {
            cursor_to = (6 - x) + (y * SCREENWIDTH);
            cursor_from = (6 - x - 1) + (y * SCREENWIDTH);
          }
          memcpy(&pixels[cursor_to], &pixels[cursor_from], sizeof(struct led_rgb));
        }
}

void addNewRow(bool scroll_to_left)
{
    if (scroll_to_left) {
      for ( int i = 0; i < CHRWIDTH; i++ ) {
        if (font_bitmap8[current_char][i] & (1 &l<t ( CHRWIDTH - position - 1))) 
          set_pixel(6, i, colors[sys_rand32_get() % 87]);
        else
          set_pixel(6, i, black); 
      }
    }
}

void main(void)
{
        int rc;
        bool stopScrolling = false; 

        strcpy(scrollText,"0123456789/:-. `~abcdefghijklmnopqrstuvwxyz");
        scroll_text_length = strlen(scrollText);
        position = 0;
        letter = 0;

        if (device_is_ready(strip)) {
                LOG_INF("Found LED strip device %s", strip->name);
        } else {
                LOG_ERR("LED strip device %s is not ready", strip->name);
                return;
        }

        if (!device_is_ready(br_uart_dev)) {
                printk("BT UART device is not ready!");
                LOG_INF("BT UART device is not ready!");
                return;
        }
        else {
                printk("Found BT UART device!");
                LOG_INF("Found BT UART device!");
        }

        if (!device_is_ready(br_uart_dev)) {
                printk("PC UART device is not ready!");
                LOG_INF("PC UART device is not ready!");
                return;
        } 
        else {
                printk("Found PC UART device!");
                LOG_INF("Found PC UART device!");
        }

        /* configure interrupt and callback to receive data */
        int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL);

        if (ret < 0) {
                if (ret == -ENOTSUP) {
                        printk("Interrupt-driven UART API support not enabled\n");
                } else if (ret == -ENOSYS) {
                        printk("UART device does not support interrupt-driven API\n");
                } else {
                        printk("Error setting UART callback: %d\n", ret);
                }   
                return;
        }   
        uart_irq_rx_enable(uart_dev);


        print_uart("Hello! I'm your echo bot.\r\n");
        print_uart("Tell me something and press enter:\r\n");


        while (1) {
              /*if (speedUP) {
                  speedUP = false;
                  stopScrolling = false;
                  speed = speed - 1;
                  if (speed < 1) 
                    speed = 1;
                }
                if (slowDOWN) {
                  slowDOWN = false;
                  speed = speed + 1;
                  if (speed > MAXSPEED) {
                    stopScrolling = true;
                    speed = MAXSPEED;
                  }
                } */
                if (!stopScrolling) {
                  position++;
                  if (!(position % 8)) {
                    position = 0;
                    letter++;
                    if (letter == scroll_text_length)
                      letter = 0;
                    current_char = 0;
                    for (int i = 0; i < NRFONTCHR; i++)
                      if (scrollText[letter] == font_chars[i])
                        current_char = i;
                  }
                  scrollMatrix(scroll_to_left);
                  addNewRow(scroll_to_left);
                }

                rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);

                if (rc) {
                        LOG_ERR("couldn't update strip: %d", rc);
                }

                k_sleep(DELAY_TIME);
        }
}