パトライト風に使いたいので、とりあえず赤で点灯数を増やして同時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



