Skip to content

Commit 9b3b16a

Browse files
committed
feat: lighting effects, calibration mode, save color in NVM
1 parent d545548 commit 9b3b16a

9 files changed

Lines changed: 291 additions & 31 deletions

File tree

.github/workflows/build.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,12 @@ jobs:
8282
with:
8383
name: zwa2_controller.hex
8484
path: artifact/zwa2_controller.hex
85+
86+
- name: Copy MAP file
87+
run: cp build/release/nc_controller_ncp.map artifact/zwa2_controller.map
88+
89+
- name: Upload MAP
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: zwa2_controller.map
93+
path: artifact/zwa2_controller.map

app.c

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ static StaticTask_t BackgroundHwTaskBuffer;
6767
static uint8_t BackgroundHwStackBuffer[HW_TASK_STACK_SIZE];
6868

6969
bool bRequestGyroMeasurement = false;
70+
bool bAwaitingConnection = false;
71+
LedEffect_t ledEffect = {0};
72+
LedMode_t ledMode = LED_MODE_STATUS;
7073

7174
static void ApplicationInitSW(void);
7275
static void ApplicationTask(SApplicationHandles *pAppHandles);
@@ -529,6 +532,11 @@ ApplicationTask(SApplicationHandles* pAppHandles)
529532

530533
static void SerialAPICommandHandler(void)
531534
{
535+
if (bAwaitingConnection) {
536+
bAwaitingConnection = false;
537+
zaf_event_distributor_enqueue_proprietary_app_event(EVENT_APP_CONNECTED, NULL);
538+
}
539+
532540
const bool handler_invoked = invoke_cmd_handler(serial_frame);
533541
if (!handler_invoked)
534542
{
@@ -794,20 +802,78 @@ zaf_event_distributor_app_proprietary(event_nc_t *event)
794802
EVENT_APP event_nc = (EVENT_APP) event->event;
795803
switch (event_nc) {
796804
case EVENT_APP_USERTASK_READY:
797-
// Indicate that the firmware is ready by enabling the LED at low power
798-
rgb_t color = {4, 0, 0};
799-
set_color_buffer(color);
805+
if (ledMode != LED_MODE_MANUAL) {
806+
// Fade slowly to white
807+
LedEffectFade_t fade = {
808+
.color = white,
809+
.brightness = 0xff,
810+
.increasing = false,
811+
.ticksPerStep = 2,
812+
.tickCounter = 0
813+
};
814+
ledEffect = (LedEffect_t) {
815+
.type = LED_EFFECT_FADE,
816+
.effect.fade = fade
817+
};
818+
bAwaitingConnection = true;
819+
}
820+
break;
821+
822+
case EVENT_APP_CONNECTED:
823+
if (ledMode != LED_MODE_MANUAL) {
824+
// Indicate that the Serial API is connected by turning the LED white solid
825+
if (ledEffect.type == LED_EFFECT_FADE) {
826+
ledEffect.effect.fade.stopAtMax = true;
827+
} else {
828+
LedEffectSolid_t solid = {
829+
.color = white,
830+
.modified = true
831+
};
832+
ledEffect = (LedEffect_t) {
833+
.type = LED_EFFECT_SOLID,
834+
.effect.solid = solid
835+
};
836+
}
837+
}
800838
break;
801839

802840
case EVENT_APP_USERTASK_GYRO_MEASUREMENT:
841+
// A gyro measurement was requested
842+
gyro_reading_t gyro_reading = event->payload->gyro_reading;
843+
844+
if (ledMode == LED_MODE_CALIBRATION) {
845+
// Indicate bad orientation (more than 20° from vertical) in calibration mode
846+
if (gyro_reading.z < -960 || gyro_reading.z > 960) {
847+
LedEffectSolid_t solid = {
848+
.color = white,
849+
.modified = true
850+
};
851+
ledEffect = (LedEffect_t) {
852+
.type = LED_EFFECT_SOLID,
853+
.effect.solid = solid
854+
};
855+
} else {
856+
if (ledEffect.type != LED_EFFECT_FADE || ledEffect.effect.fade.color.R != 0xff) {
857+
LedEffectFade_t fade = {
858+
.color = red,
859+
.brightness = 0xff,
860+
.increasing = false,
861+
.ticksPerStep = 1,
862+
.tickCounter = 0
863+
};
864+
ledEffect = (LedEffect_t) {
865+
.type = LED_EFFECT_FADE,
866+
.effect.fade = fade
867+
};
868+
}
869+
}
870+
}
871+
803872
if (!bRequestGyroMeasurement) {
804873
return;
805874
}
806875
bRequestGyroMeasurement = false;
807876

808-
// A gyro measurement was requested
809-
gyro_reading_t gyro_reading = event->payload->gyro_reading;
810-
811877
uint8_t cmd[8];
812878
uint8_t i=0;
813879
cmd[i++] = NABU_CASA_GYRO_MEASURE;
@@ -824,6 +890,69 @@ zaf_event_distributor_app_proprietary(event_nc_t *event)
824890
);
825891
break;
826892

893+
case EVENT_APP_USERTASK_TICK_LED: {
894+
switch (ledEffect.type) {
895+
case LED_EFFECT_SOLID: {
896+
// For solid LED effect, set the color once
897+
if (ledEffect.effect.solid.modified) {
898+
ledEffect.effect.solid.modified = false;
899+
set_color_buffer(ledEffect.effect.solid.color);
900+
}
901+
break;
902+
}
903+
904+
case LED_EFFECT_FADE: {
905+
// For fading, change the brightness every N ticks,
906+
// and update the color
907+
LedEffectFade_t fade = ledEffect.effect.fade;
908+
909+
if (fade.brightness > 0xe0 && fade.stopAtMax) {
910+
// Switch to solid mode
911+
LedEffectSolid_t solid = {
912+
.color = fade.color,
913+
.modified = true
914+
};
915+
ledEffect = (LedEffect_t) {
916+
.type = LED_EFFECT_SOLID,
917+
.effect.solid = solid
918+
};
919+
break;
920+
}
921+
922+
if (fade.tickCounter == 0) {
923+
if (fade.increasing) {
924+
fade.brightness++;
925+
if (fade.brightness == 0xff) {
926+
fade.increasing = false;
927+
}
928+
} else {
929+
fade.brightness--;
930+
if (fade.brightness == 0) {
931+
fade.increasing = true;
932+
}
933+
}
934+
935+
uint16_t r = ((uint16_t) fade.color.R) * ((uint16_t) fade.brightness) / 0xff;
936+
uint16_t g = ((uint16_t) fade.color.G) * ((uint16_t) fade.brightness) / 0xff;
937+
uint16_t b = ((uint16_t) fade.color.B) * ((uint16_t) fade.brightness) / 0xff;
938+
939+
rgb_t color = {
940+
(uint8_t) g,
941+
(uint8_t) r,
942+
(uint8_t) b
943+
};
944+
945+
set_color_buffer(color);
946+
}
947+
948+
fade.tickCounter = (fade.tickCounter + 1) % fade.ticksPerStep;
949+
ledEffect.effect.fade = fade;
950+
break;
951+
}
952+
}
953+
break;
954+
}
955+
827956
default:
828957
// Nothing to do
829958
break;
@@ -957,6 +1086,23 @@ ApplicationInit(
9571086
initWs2812();
9581087
initqma6100p();
9591088

1089+
// Try to restore the user-defined color from NVM
1090+
NabuCasaLedStorage_t ledStorage = {0};
1091+
if (
1092+
SerialApiNvmReadAppData(NC_APPDATA_OFFSET_LED, (uint8_t *)&ledStorage, sizeof(ledStorage))
1093+
&& ledStorage.valid
1094+
) {
1095+
rgb_t color = {
1096+
.G = ledStorage.g,
1097+
.R = ledStorage.r,
1098+
.B = ledStorage.b
1099+
};
1100+
ledMode = LED_MODE_MANUAL;
1101+
set_color_buffer(color);
1102+
} else {
1103+
set_color_buffer(white);
1104+
}
1105+
9601106
/*************************************************************************************
9611107
* CREATE USER TASKS - ZW_ApplicationRegisterTask() and ZW_UserTask_CreateTask()
9621108
*************************************************************************************

app.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include <ZW_controller_api.h>
3636
#endif
3737

38+
#include "drivers/ws2812.h"
39+
3840
/* Serial API version */
3941
#define SERIAL_API_VER 10
4042

@@ -270,4 +272,37 @@ extern void ApplicationNodeUpdate(uint8_t bStatus, uint16_t nodeID, uint8_t *pCm
270272

271273
extern uint8_t compl_workbuf[BUF_SIZE_TX];
272274

275+
typedef struct LedEffectSolid {
276+
rgb_t color;
277+
bool modified;
278+
} LedEffectSolid_t;
279+
280+
typedef struct LedEffectFade {
281+
rgb_t color;
282+
uint8_t brightness;
283+
uint8_t ticksPerStep;
284+
uint8_t tickCounter;
285+
bool increasing;
286+
bool stopAtMax;
287+
} LedEffectFade_t;
288+
289+
typedef enum {
290+
LED_EFFECT_SOLID,
291+
LED_EFFECT_FADE
292+
} LedEffectType_t;
293+
294+
typedef struct {
295+
LedEffectType_t type;
296+
union {
297+
LedEffectSolid_t solid;
298+
LedEffectFade_t fade;
299+
} effect;
300+
} LedEffect_t;
301+
302+
typedef enum {
303+
LED_MODE_STATUS = 0,
304+
LED_MODE_CALIBRATION = 1,
305+
LED_MODE_MANUAL = 2,
306+
} LedMode_t;
307+
273308
#endif /* _SERIALAPPL_H_ */

app_events.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ typedef enum EVENT_APP_NC
2121
*/
2222
EVENT_APP_USERTASK_READY,
2323
EVENT_APP_USERTASK_GYRO_MEASUREMENT,
24+
EVENT_APP_USERTASK_TICK_LED,
25+
EVENT_APP_CONNECTED,
2426
}
2527
EVENT_APP;
2628

app_hw_task.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
* MACROS
3333
***************************************************************/
3434

35-
#define USER_TASK_WAKEUP_PERIOD 500
35+
#define USER_TASK_WAKEUP_PERIOD 2 // needed to pulse LEDs smoothly
36+
#define GYRO_MEASURE_PERIOD 500
3637

3738
/****************************************************************
3839
* FORWARD DECLARATIONS (none preferred)
@@ -55,19 +56,30 @@ static zpal_pm_handle_t task_power_lock;
5556
*/
5657
NO_RETURN static void executeThread(void)
5758
{
59+
TickType_t last_gyro_measure = 0;
5860
for (;;)
5961
{
6062
zpal_pm_stay_awake(task_power_lock, 0);
6163

62-
int16_t acc_data[3];
63-
qma6100p_read_raw_xyz(acc_data);
64-
gyro_reading_t gyro_reading = {
65-
.x = acc_data[0],
66-
.y = acc_data[1],
67-
.z = acc_data[2]};
68-
nc_event_payload_t payload = {
69-
.gyro_reading = gyro_reading};
70-
zaf_event_distributor_enqueue_proprietary_app_event(EVENT_APP_USERTASK_GYRO_MEASUREMENT, &payload);
64+
TickType_t now = xTaskGetTickCount();
65+
66+
// Measure the gyro when it's time
67+
if (now - last_gyro_measure >= pdMS_TO_TICKS(GYRO_MEASURE_PERIOD))
68+
{
69+
int16_t acc_data[3];
70+
qma6100p_read_raw_xyz(acc_data);
71+
gyro_reading_t gyro_reading = {
72+
.x = acc_data[0],
73+
.y = acc_data[1],
74+
.z = acc_data[2]};
75+
nc_event_payload_t payload = {
76+
.gyro_reading = gyro_reading};
77+
zaf_event_distributor_enqueue_proprietary_app_event(EVENT_APP_USERTASK_GYRO_MEASUREMENT, &payload);
78+
last_gyro_measure = now;
79+
}
80+
81+
// Trigger a new LED tick
82+
zaf_event_distributor_enqueue_proprietary_app_event(EVENT_APP_USERTASK_TICK_LED, NULL);
7183

7284
zpal_pm_cancel(task_power_lock);
7385
vTaskDelay(pdMS_TO_TICKS(USER_TASK_WAKEUP_PERIOD));

0 commit comments

Comments
 (0)