/* * Created on: Jul 29, 2024 */ #include #include "ledprog_pep.h" #include "adc.h" #include "config.h" #include "led.h" #include "lis2dw.h" #include "rand.h" enum { PEP_0, PEP_1, PEP_2, PEP_3, PEP_4, PEP_5, PEP_6, PEP_7 }; static uint16_t pep_rnd; static uint8_t pep_work[4]; static uint16_t rand_program = 0; static uint32_t rand_timeout = 0; static uint16_t rand_flash_timeout; /* * my little pepper is flashing you * can be instant (all on/off) or fading in/out * todo: implement fading mode * if flash rate is 0, then just stay solid * * work global: * 0: flash timeout * 1: flash state * 2: fade timeout * 3: fade state */ #define PEP_0_CONF_BRIGHTNESS 0 #define PEP_0_CONF_FLASH_RATE 1 #define PEP_0_CONF_FADE_ENA 2 #define PEP_0_CONF_FADE_RATE 3 static void pep_0_flash(uint8_t tick) { uint8_t i; uint16_t out = userconf.pep_conf[PEP_0][PEP_0_CONF_BRIGHTNESS]; uint8_t flash = userconf.pep_conf[PEP_0][PEP_0_CONF_FLASH_RATE]; if (!flash) { // just force solid on pep_work[0] = pep_work[1] = 0; flash = 1; } if (!pep_work[0]) { pep_work[1] ^= 0x1; if (pep_work[1] & 1) { for (i = 0; i < LED_PEP_NOTOP; i++) { led.section.pep[i] = out; } for (i = LED_PEP_NOTOP; i < LED_PEP_COUNT; i++) { led.section.pep[i] = out >> 1; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = out; } } else { for (i = 0; i < LED_PEP_COUNT; i++) { led.section.pep[i] = 0; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = 0; } } led_matrix_is_updated(); pep_work[0] = flash; } pep_work[0]--; } /* * pepper is lit with random slightly varying intensity */ static void pep_1_sizzle(uint8_t tick) { uint8_t i; uint8_t trig; uint16_t rnd; uint32_t rnd2; trig = prng_get8(); rnd = prng_get8(); rnd2 = prng_get32(); // are we going to spike an LED? trig = (trig > 0xfa) ? 1 : 0; if (trig) { // which LED? rnd *= LED_PEP_NOTOP; rnd >>= 8; } // do some sizzles for (i = 0; i < LED_PEP_NOTOP; i++) { if (trig && (rnd == i)) { // go bright on this LED led.section.pep[i] = 0xcf; // also on the prior LED if (i) led.section.pep[i - 1] = 0x80; else if (i < LED_PEP_NOTOP - 1) led.section.pep[i + 1] = 0x80; } else { if (led.section.pep[i] < 4) led.section.pep[i]++; else if (led.section.pep[i] > 36) led.section.pep[i]--; else { if (rnd2 & 1) led.section.pep[i]++; else led.section.pep[i]--; } rnd2 >>= 1; } } // the hat just gets lit normally for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = 0x3f; } led_matrix_is_updated(); } /* * trail chase around the pepper * pepper is on slightly dim at all times * * no user config at this time * * work global: * 0: loop state */ #define PEP_2_CONF_BRIGHTNESS 0 #define PEP_2_CONF_LOOP_RATE 1 #define PEP_2_CONF_DECAY_RATE 3 static void pep_2_loops(uint8_t tick) { uint8_t i; uint16_t top, bot; uint8_t brite = userconf.pep_conf[PEP_2][PEP_2_CONF_BRIGHTNESS]; uint8_t rate = userconf.pep_conf[PEP_2][PEP_2_CONF_LOOP_RATE]; uint8_t decay = userconf.pep_conf[PEP_2][PEP_2_CONF_DECAY_RATE]; if (pep_work[0]) { pep_work[0]--; } else { pep_work[0] = rate; pep_work[1]++; pep_work[1] &= 0x3f; } bot = pep_work[1] * LED_PEP_COUNT; bot >>= 6; for (i = 0; i < LED_PEP_COUNT; i++) { if (i == bot) led.section.pep[i] = brite; else { if (led.section.pep[i] >= decay) led.section.pep[i] -= decay; else led.section.pep[i] = 0; } } top = pep_work[1] * LED_HAT_COUNT; top >>= 6; for (i = 0; i < LED_HAT_COUNT; i++) { if (i == top) led.section.hat[i] = brite; else { if (led.section.hat[i] >= decay) led.section.hat[i] -= decay; else led.section.hat[i] = 0; } } led_matrix_is_updated(); } /* * pepper is a "sign" that flickers on and off * sometimes the entire sign goes off, then each * segment, upper and lower, turns on * after both segments are on the separator goes off */ static uint8_t flicker_count[2]; static uint8_t flicker_delay[2]; static uint8_t flicker_timer[2]; static void pep_3_neon_sign(uint8_t tick) { uint8_t i; uint16_t rnd; uint8_t target; if (tick & 4) { rnd = prng_get8(); if (rnd >= 0xfc) { // do a flicker target = rnd & 1; flicker_count[target] = prng_scale16(1, 4) << 1; flicker_delay[target] = prng_scale16(6, 120); if (rnd & 2) { flicker_count[target ^ 1] = flicker_count[target]; flicker_delay[target ^ 1] = flicker_delay[target]; } } } // todo: implement flickering on/off // like a flourescent tube warming up or failing for (i = 0; i < 2; i++) { if (flicker_count[i]) { if (flicker_timer[i]) { flicker_timer[i]--; } else { flicker_count[i]--; flicker_timer[i] = flicker_delay[i]; } } } for (i = 0; i < LED_PEP_COUNT; i++) { led.section.pep[i] = flicker_count[0] & 1 ? 0 : 0x80; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = flicker_count[1] & 1 ? 0 : 0x60; } led_matrix_is_updated(); } /* * pepper hat and pepper alternate * can be a nice fade or can be immediate */ #define PEP_4_CONF_BRIGHTNESS 0 #define PEP_4_CONF_FLASH_RATE 1 #define PEP_4_CONF_LOW_BRITE 2 static void pep_4_alternate(uint8_t tick) { uint8_t i; uint8_t hi = userconf.pep_conf[PEP_4][PEP_4_CONF_BRIGHTNESS]; uint8_t lo = userconf.pep_conf[PEP_4][PEP_4_CONF_LOW_BRITE]; if (!pep_work[0]) { for (i = 0; i < LED_PEP_COUNT; i++) { led.section.pep[i] = (pep_work[1] & 1) ? hi : lo; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = (pep_work[1] & 1) ? lo : hi; } pep_work[1] ^= 0x1; led_matrix_is_updated(); pep_work[0] = userconf.pep_conf[PEP_4][PEP_4_CONF_FLASH_RATE]; } else { pep_work[0]--; } } /* * pepper slowly gets eateded * but it regenerates because it is pepper * * work global: * 0: state index * 1: eat / regen step * 2: delay step */ static const uint8_t nom_map[5][2] = { {14, 21}, {12, 23}, { 9, 24}, { 7, 27}, { 3, 28} }; uint16_t nom_timeout = 0; static void pep_5_nom(uint8_t tick) { uint8_t i; uint8_t start, end; switch (pep_work[0]) { case 0: case 2: { // wait a while if (!nom_timeout) { // just got here; set a new random timeout nom_timeout = 0x3ff - prng_get8(); // also set the pepper to be on for (i = LED_PEP_NOTOP; i < LED_PEP_COUNT; i++) { led.section.pep[i] = 0x80; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = 0x60; } led_matrix_is_updated(); } else { // wait around for a little while nom_timeout--; if (!nom_timeout) { // done here pep_work[1] = 0; pep_work[0]++; pep_work[0] &= 0x3; } } break; } case 1: { // regen the pepper if (led.section.pep[pep_work[1]] >= (0x80 - 4)) { // next segment pep_work[1]++; if (pep_work[1] > (LED_PEP_NOTOP - pep_work[1])) { // we're done regenerating pep_work[0]++; } } else { led.section.pep[pep_work[1]] += 4; if (pep_work[1]) { led.section.pep[LED_PEP_NOTOP - pep_work[1]] = led.section.pep[pep_work[1]]; } led_matrix_is_updated(); } break; } case 3: { // eat the pepper // eat at about one bite per second if ((tick & 0x7f) != 0) break; start = 0; if (!pep_work[1]) { end = 0; } else if (pep_work[1] < 6) { start = nom_map[pep_work[1] - 1][0] - 1; end = nom_map[pep_work[1] - 1][1] - 1; } else { end = LED_PEP_NOTOP - 1; } // set pepper body LEDs to initial on state (or off if pepper is fully eated) for (i = 0; i < LED_PEP_NOTOP; i++) { led.section.pep[i] = (pep_work[1] < 6) ? 0x80 : 0; } // in this mode we need to light up the top pepper line as well as the hat for (i = LED_PEP_NOTOP; i < 64; i++) { led.all[i] = 0x60; } // clear eated pepper portions, gonna cry if (end) { for (i = start; i <= end; i++) { led.section.pep[i] = 0; } } led_matrix_is_updated(); pep_work[1]++; if (pep_work[1] > 6) pep_work[0] = 0; break; } } } static void pep_6_random(uint8_t tick) { if (rand_flash_timeout <= 2) { // light lsens adc_set_mode_lsens(LSENS_OUTPUT); LSENS_PORT->BSHR = LSENS_A_PIN; LSENS_PORT->BCR = LSENS_K_PIN; if (!rand_flash_timeout) rand_flash_timeout = 600; } else { if (adc_get_mode_lsens() == LSENS_OUTPUT) { // restore lsens LSENS_PORT->BCR = LSENS_A_PIN; adc_set_mode_lsens(LSENS_READING_IDLE); } } rand_flash_timeout--; if (!rand_timeout) { rand_timeout = prng_scale16(20, 150); rand_timeout <<= 7; rand_program = 6 * prng_get8(); rand_program >>= 8; ledprog_pep_init(); } rand_timeout--; ledprog_pep[rand_program](tick); } /* * make a sharp point at whatever direction is the ground * note: probably won't be done before con */ static void pep_6_accelerometer(uint8_t tick) { // not done in time for con } /* * adjust pepper level based on ambient temperature * ~70F or below: minimum * ~94F or above: spicy */ static void pep_7_heat(uint8_t tick) { // not done in time for con } /* const void (*ledprog_pep[8])(uint8_t) = { (const void (*)(uint8_t))pep_0_flash, (const void (*)(uint8_t))pep_1_sizzle, (const void (*)(uint8_t))pep_2_loops, (const void (*)(uint8_t))pep_3_neon_sign, (const void (*)(uint8_t))pep_4_alternate, (const void (*)(uint8_t))pep_5_nom, (const void (*)(uint8_t))pep_6_accelerometer, (const void (*)(uint8_t))pep_7_heat }; */ void (*ledprog_pep[8])(uint8_t) = { pep_0_flash, pep_1_sizzle, pep_2_loops, pep_3_neon_sign, pep_4_alternate, pep_5_nom, pep_6_random //pep_6_accelerometer, //pep_7_heat }; void ledprog_pep_init() { uint8_t i; pep_rnd = prng_get16(); // reset LEDs for (i = 0; i < LED_PEP_COUNT; i++) { led.section.pep[i] = 0; } for (i = 0; i < LED_HAT_COUNT; i++) { led.section.hat[i] = 0; } // global program initialization for (i = 0; i < 4; i++) { pep_work[i] = 0; } // per-program initialization nom_timeout = 0; if ((userconf.pep_prog_ena_map & ~0x80) != 6) { rand_flash_timeout = 3; } // there is none on this badge }