At the very beginning, I wanted the device to rotate in one direction, so I used an infrared sensor. Later, I realized it was too heavy and couldn’t rotate smoothly. That’s how it eventually became an LED POV (persistence-of-vision) wand.

The infrared sensor can actually be replaced with a vibration switch, which makes it possible to detect motion direction more easily.

Physical demo

Front view: This is what it looks like from the front. The circuit is very simple — each LED is driven directly via GPIO. There are 16 LEDs in total, and the M61 development board is plugged directly onto the pin headers.

Powered on: I didn’t add a power switch, so the device starts running as soon as it’s powered on.

 

In action: Shake it, shake it harder… (pretty tiring)

How it works

That’s basically it. The principle is actually very simple. Using a font extraction tool, the dot-matrix data is generated first. Then, each column and each bit is read sequentially to control whether an LED is on or off.

By continuously looping this process, the POV wand effect is achieved. There’s not much more to it — simple and straightforward.

main.c

1. #include "bflb_mtimer.h"

2. #include "bflb_gpio.h"

3. #include "board.h"

4. 

5. struct bflb_device_s *gpio;

6. 

7. uint8_t data[5][32] = {

8.               {0x80,0x00,0x90,0x80,0x8C,0x80,0x84,0x84,0x84,0x46,0x84,0x49,0xF5,0x28,0x86,0x10,0x84,0x10,0x84,0x2C,0x84,0x23,0x84,0x40,0x94,0x80,0x8C,0x00,0x80,0x00,0x00,0x00},/*"安",0*/

9.               {0x00,0x01,0x80,0x00,0x60,0x00,0xF8,0xFF,0x07,0x00,0x00,0x00,0x04,0x00,0x24,0xF9,0x24,0x49,0x25,0x49,0x26,0x49,0x24,0x49,0x24,0x49,0x24,0xF9,0x04,0x00,0x00,0x00},/*"信",1*/

10.               {0x00,0x00,0x02,0x00,0x02,0x00,0xF2,0x0F,0x12,0x04,0x12,0x04,0x12,0x04,0xF2,0x0F,0x02,0x00,0x02,0x40,0x02,0x80,0xFE,0x7F,0x02,0x00,0x02,0x00,0x02,0x00,0x00,0x00},/*"可",2*/

11.               {0x24,0x08,0x24,0x06,0xA4,0x01,0xFE,0xFF,0xA3,0x00,0x22,0x01,0x00,0x04,0x22,0x04,0xCC,0x04,0x00,0x04,0x00,0x04,0xFF,0xFF,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x00},/*"科",3*/

12.               {0x10,0x04,0x10,0x44,0x10,0x82,0xFF,0x7F,0x10,0x01,0x90,0x80,0x08,0x80,0x88,0x40,0x88,0x43,0x88,0x2C,0xFF,0x10,0x88,0x28,0x88,0x46,0x88,0x81,0x08,0x80,0x00,0x00},/*"技",4*/

13.               

14.               };

15. 

16. uint8_t leds[] = {10,11,12,13,14,15,16,17,18,19,20,23,24,25,26,27};

17. 

18. 

19. void led_play(uint8_t idx, int delay){

20.     uint8_t index = 0;

21.     for(uint8_t i=0;i<16;i++){

22.         for(uint8_t n=0;n<8;n++){

23.             uint8_t d = data[idx][index];

24.             if(((d>>n) & 0x01) == 0){

25.                 bflb_gpio_reset(gpio,leds[n]);

26.                

27.             }else{

28.                 bflb_gpio_set(gpio,leds[n]);

29.                

30.             }

31.         }

32. 

33.         index++;

34.         for(uint8_t n=0;n<8;n++){

35.             uint8_t d = data[idx][index];

36.             if(((d>>n) & 0x01) == 0){

37.                 bflb_gpio_reset(gpio,leds[n+8]);

38. 

39.             }else{

40.                 bflb_gpio_set(gpio,leds[n+8]);

41.    

42.             }

43.         }

44. 

45.         index++;

46.         bflb_mtimer_delay_us(delay);

47.     }

48. 

49. }

50. 

51. void del_led(uint8_t idx, int delay){

52.     uint8_t index = 30;

53.     for(uint8_t i=0;i<16;i++){

54.         for(uint8_t n=0;n<8;n++){

55.             uint8_t d = data[idx][index];

56.             if(((d>>n) & 0x01) == 0){

57.                 bflb_gpio_reset(gpio,leds[n]);

58.                

59.             }else{

60.                 bflb_gpio_set(gpio,leds[n]);

61.                

62.             }

63.         }

64. 

65.         index++;

66.         for(uint8_t n=0;n<8;n++){

67.             uint8_t d = data[idx][index];

68.             if(((d>>n) & 0x01) == 0){

69.                 bflb_gpio_reset(gpio,leds[n+8]);

70. 

71.             }else{

72.                 bflb_gpio_set(gpio,leds[n+8]);

73.    

74.             }

75.         }

76. 

77.         index -= 3;

78.         bflb_mtimer_delay_us(delay);

79.     }

80. 

81. }

82. 

83. int main(void)

84. {

85.     int dey = 800;

86.     board_init();

87.     gpio = bflb_device_get_by_name("gpio");

88.     for(uint8_t i=0;i<16;++i){

89.         bflb_gpio_init(gpio, leds[i], GPIO_OUTPUT|GPIO_PULLDOWN);

90.         bflb_gpio_reset(gpio,leds[i]);

91.     }

92. 

93.     printf("hello world\r\n");

94.     while (1) {

95.         for(int i=0;i<5;i++){

96.             led_play(i,dey);

97.         }

98.         // for(int j=5;j>0;j--){

99.         //     del_led(j,dey);

100.         // }

101. 

102.     }

103. }


Conclusion

That’s all for this project. The code is quite simple: first, store the font data in arrays, then iterate through each character in a loop, and keep refreshing continuously.

This demo only displays fixed content, but it can be expanded further. For example, we could use a mobile app to connect to the M61 via Bluetooth or Wi-Fi to update the displayed text or images dynamically. We could also increase the number of LEDs and turn it into a rotating LED display.

If you’re interested, we can definitely explore and improve it together.