ソフト基板LEDを筒にするケース -3-

 パトライト風に使いたいので、とりあえず赤で点灯数を増やして同時12個にする。これを少しズラしてクルクル回る感じを出してみる。

 ニンゲンの目は残像が残りやすいので、もっと連続して全部点灯している感じに見えなくもない。3/12しかついていないのにな。回転はもっとゆっくりでもいいかな? でも、遅くすると緊迫感がなくなる?

パトライト風に - YouTube
https://www.youtube.com/watch?v=3KfWQSx9XsI

 以下はコードだけ。末尾には何も書いてない。

 #include <stdbool.h>

 #include <stdint.h>

 #include "freertos/FreeRTOS.h"

 #include "freertos/task.h"

 #include "esp_err.h"

 #include "esp_log.h"

 #include "led_strip.h"

 #include "led_strip_rmt.h"

 /*

 * ATOMS3 Lite Grove

 *

 * 赤:5V

 * 黒:GND

 * 黄:GPIO2

 */

 #define LED_STRIP_GPIO 2

 /*

 * 4×12 LEDマトリックスを筒状に配置

 *

 * 周方向:12列

 * 高さ方向:4段

 */

 #define CYLINDER_AROUND 12U

 #define CYLINDER_LEVELS 4U

 #define LED_STRIP_LED_NUM (CYLINDER_AROUND * CYLINDER_LEVELS)

 /*

 * 各段に表示する3灯の明るさ。

 *

 * 隣接:10

 * 中央:20

 * 隣接:10

 */

 #define CENTER_BRIGHTNESS 20U

 #define ADJACENT_BRIGHTNESS 10U

 /*

 * 周方向へ1位置移動する間隔。

 *

 * 小さいほど高速になる。

 */

 #define ROTATION_INTERVAL_MS 20U

 /*

 * 段ごとの点灯位置のずれ。

 *

 * 1:

 * 各段を1灯ずつずらす。

 *

 * 2:

 * 各段を2灯ずつずらす。

 */

 #define LEVEL_PHASE_STEP 1U

 /*

 * 0:

 * 上の段ほど進行方向へずれる。

 *

 * 1:

 * 上の段ほど進行方向と逆へずれる。

 */

 #define REVERSE_LEVEL_PHASE 0

 /*

 * 上下が逆の場合は0に変更する。

 */

 #define LED_INPUT_SIDE_IS_BOTTOM 1

 /*

 * 回転方向が逆の場合は0に変更する。

 */

 #define INCREASING_IS_CLOCKWISE 1

 static const char *TAG = "patlite_led";

 /*

 * 筒状の座標を実際のLED番号へ変換する。

 *

 * around:

 * 周方向。0~11。

 *

 * level:

 * 高さ方向。0~3。

 * 0が最下段。

 *

 * マトリックスは4灯ごとのS字配線を想定。

 *

 * 列0: 0 1 2 3

 * 列1: 7 6 5 4

 * 列2: 8 9 10 11

 * 列3:15 14 13 12

 *

 * 最下段を周方向にたどるLED番号:

 *

 * 0, 7, 8, 15, 16, 23,

 * 24, 31, 32, 39, 40, 47

 */

 static uint32_t cylinder_to_led_index(

 uint32_t around,

 uint32_t level)

 {

 uint32_t physical_around;

 uint32_t physical_level;

 if (INCREASING_IS_CLOCKWISE) {

 physical_around = around;

 } else {

 physical_around =

 CYLINDER_AROUND - 1U - around;

 }

 if (LED_INPUT_SIDE_IS_BOTTOM) {

 physical_level = level;

 } else {

 physical_level =

 CYLINDER_LEVELS - 1U - level;

 }

 /*

 * 偶数列は正方向。

 */

 if ((physical_around & 1U) == 0U) {

 return physical_around * CYLINDER_LEVELS

 + physical_level;

 }

 /*

 * 奇数列は逆方向。

 */

 return physical_around * CYLINDER_LEVELS

 + (CYLINDER_LEVELS - 1U - physical_level);

 }

 /*

 * 全LEDを内部バッファ上で消灯する。

 *

 * この関数ではled_strip_refresh()を呼ばない。

 */

 static void clear_led_buffer(

 led_strip_handle_t strip)

 {

 for (uint32_t index = 0;

 index < LED_STRIP_LED_NUM;

 ++index) {

 ESP_ERROR_CHECK(

 led_strip_set_pixel(

 strip,

 index,

 0U,

 0U,

 0U

 )

 );

 }

 }

 /*

 * 指定された段と周方向位置を赤色で点灯する。

 */

 static void set_red_pixel(

 led_strip_handle_t strip,

 uint32_t level,

 uint32_t around,

 uint8_t brightness)

 {

 const uint32_t led_index =

 cylinder_to_led_index(

 around,

 level

 );

 ESP_ERROR_CHECK(

 led_strip_set_pixel(

 strip,

 led_index,

 brightness, /* 赤 */

 0U, /* 緑 */

 0U /* 青 */

 )

 );

 }

 /*

 * 各段の中心位置を計算する。

 *

 * base_position:

 * 最下段を基準にした現在位置。

 *

 * level:

 * 高さ方向の段番号。

 */

 static uint32_t get_level_center_position(

 uint32_t base_position,

 uint32_t level)

 {

 const uint32_t offset =

 (level * LEVEL_PHASE_STEP)

 % CYLINDER_AROUND;

 #if REVERSE_LEVEL_PHASE

 return (

 base_position

 + CYLINDER_AROUND

  • offset  ) % CYLINDER_AROUND;

 #else

 return (

 base_position

 + offset

 ) % CYLINDER_AROUND;

 #endif

 }

 /*

 * 指定された段に3灯の光帯を設定する。

 *

 * 前方:輝度10

 * 中央:輝度20

 * 後方:輝度10

 */

 static void set_level_light_band(

 led_strip_handle_t strip,

 uint32_t level,

 uint32_t center_position)

 {

 const uint32_t previous_position =

 (

 center_position

 + CYLINDER_AROUND

  • 1U  ) % CYLINDER_AROUND;

 const uint32_t next_position =

 (

 center_position

 + 1U

 ) % CYLINDER_AROUND;

 set_red_pixel(

 strip,

 level,

 previous_position,

 ADJACENT_BRIGHTNESS

 );

 set_red_pixel(

 strip,

 level,

 center_position,

 CENTER_BRIGHTNESS

 );

 set_red_pixel(

 strip,

 level,

 next_position,

 ADJACENT_BRIGHTNESS

 );

 }

 /*

 * 全4段を同時に表示する。

 *

 * 各段の中心位置をずらすことで、

 * 斜めの赤い光帯を作る。

 */

 static void show_patlite_frame(

 led_strip_handle_t strip,

 uint32_t base_position)

 {

 clear_led_buffer(strip);

 for (uint32_t level = 0;

 level < CYLINDER_LEVELS;

 ++level) {

 const uint32_t center_position =

 get_level_center_position(

 base_position,

 level

 );

 set_level_light_band(

 strip,

 level,

 center_position

 );

 }

 ESP_ERROR_CHECK(

 led_strip_refresh(strip)

 );

 }

 /*

 * espressif/led_strip 3.x用の初期化。

 */

 static led_strip_handle_t create_led_strip(void)

 {

 led_strip_handle_t strip = NULL;

 const led_strip_config_t strip_config = {

 .strip_gpio_num = LED_STRIP_GPIO,

 .max_leds = LED_STRIP_LED_NUM,

 .led_model = LED_MODEL_WS2812,

 /*

 * WS2812Bの一般的な色成分順。

 */

 .color_component_format =

 LED_STRIP_COLOR_COMPONENT_FMT_GRB,

 .flags = {

 .invert_out = false,

 },

 };

 const led_strip_rmt_config_t rmt_config = {

 .clk_src = RMT_CLK_SRC_DEFAULT,

 /*

 * 10MHz、1カウント100ns。

 */

 .resolution_hz =

 10U * 1000U * 1000U,

 /*

 * 自動設定。

 */

 .mem_block_symbols = 0,

 .flags = {

 .with_dma = false,

 },

 };

 ESP_ERROR_CHECK(

 led_strip_new_rmt_device(

 &strip_config,

 &rmt_config,

 &strip

 )

 );

 ESP_ERROR_CHECK(

 led_strip_clear(strip)

 );

 return strip;

 }

 void app_main(void)

 {

 led_strip_handle_t strip =

 create_led_strip();

 ESP_LOGI(

 TAG,

 "Red patlite animation start: around=%u levels=%u leds=%u",

 (unsigned int)CYLINDER_AROUND,

 (unsigned int)CYLINDER_LEVELS,

 (unsigned int)LED_STRIP_LED_NUM

 );

 while (true) {

 /*

 * 12位置を繰り返し周回する。

 */

 for (uint32_t base_position = 0;

 base_position < CYLINDER_AROUND;

 ++base_position) {

 show_patlite_frame(

 strip,

 base_position

 );

 vTaskDelay(

 pdMS_TO_TICKS(

 ROTATION_INTERVAL_MS

 )

 );

 }

 }

 }


オリジナル投稿:
ソフト基板LEDを筒にするケース -3-|kinneko|pixivFANBOX
https://kinneko.fanbox.cc/posts/12248178