Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4621978
fw/apps/prf/mfg_mag: simplify test
gmarull Mar 20, 2026
23feafe
fw/apps/prf: remove calibrate display mfg test
gmarull Mar 20, 2026
2735f90
fw/mfg: remove legacy test recording
gmarull Mar 20, 2026
862a8b4
fw/apps/prf/mfg_menu: split tests/extras
gmarull Mar 20, 2026
55440d8
fw/apps/prf/mfg_extras_menu: remove Test HRM
gmarull Mar 20, 2026
27ecd12
fw/apps/prf/mfg_menu: drop spalding legacy
gmarull Mar 20, 2026
04dc8ce
fw/apps/prf/mfg_test_result: add RAM-based test result tracking
gmarull Mar 20, 2026
05a55f2
fw/apps/prf/mfg_button: report test result
gmarull Mar 20, 2026
a86ced6
fw/apps/prf/mfg_display: prompt pass/fail after patterns
gmarull Mar 20, 2026
2532fc2
fw/apps/prf/mfg_touch: add timed pass/fail and result reporting
gmarull Mar 20, 2026
b78455e
fw/apps/prf/mfg_test_menu: auto-advance selection after test
gmarull Mar 20, 2026
3c0a788
fw/apps/prf/mfg_backlight: prompt pass/fail after patterns
gmarull Mar 23, 2026
1521edc
fw/apps/prf/mfg_accel: simplify to variation-based test
gmarull Mar 23, 2026
b09a0ce
fw/apps/prf/mfg_accel: show result for 3s then auto-close
gmarull Mar 23, 2026
07d3771
fw/apps/prf/mfg_mag: show result for 3s then auto-close
gmarull Mar 23, 2026
df2afb7
fw/apps/prf/mfg_speaker: prompt pass/fail after 5s playback
gmarull Mar 23, 2026
885c189
fw/apps/prf/mfg_mic_getafix: show result for 3s then auto-close
gmarull Mar 23, 2026
8d10065
fw/apps/prf/mfg_mic_obelix: prompt pass/fail after playback
gmarull Mar 23, 2026
e37b4ed
fw/apps/prf/mfg_als: show result for 3s then auto-close
gmarull Mar 23, 2026
54a0cfe
fw/apps/prf/mfg_vibration: add result reporting and confirm dialog
gmarull Mar 23, 2026
f73bd85
fw/apps/prf/mfg_hrm_ctr_leakage: show result for 3s then auto-close
gmarull Mar 23, 2026
52c9cd0
fw/apps/prf/mfg_program_color: record result and auto-close after 3s
gmarull Mar 23, 2026
9e6e211
fw/apps/prf/mfg_charge: record result on back press and exit
gmarull Mar 23, 2026
7d3e5fa
fw/apps/prf/mfg_discharge: add pass/fail criteria and result reporting
gmarull Mar 23, 2026
9dc4c92
fw/apps/prf/mfg_adv: add BLE advertising test
gmarull Mar 24, 2026
accd410
fw/apps/prf: move aging test to extras menu
gmarull Mar 23, 2026
1b2baaf
fw/apps/prf/mfg_qr_results: add QR code results app
gmarull Mar 23, 2026
cfde95d
fw/apps/prf/mfg_backlight: guard color steps with HAS_COLOR_BACKLIGHT
gmarull Mar 24, 2026
8d3eccf
fw/apps/prf/mfg_als: tweak getafix range
gmarull Mar 23, 2026
7179ade
fw/apps/prf/mfg_charge,mfg_discharge: allow exit with select button w…
gmarull Mar 24, 2026
17d0342
fw/apps/prf/mfg_test_menu: auto-advance to next test on completion
gmarull Mar 24, 2026
73d14a5
fw/apps/prf/mfg_charge: allow exit with back button in any state
gmarull Mar 24, 2026
fa4f599
fw/wscript: exclude touch mfg test if not needed
gmarull Mar 24, 2026
60c1420
fw/apps/prf/mfg_test_menu: stop on failed test for retry
gmarull Mar 31, 2026
5486047
fw/apps/prf: reduce result display time
gmarull Mar 31, 2026
04d9674
fw/apps/prf/mfg_als: reduce countdown/sample times
gmarull Mar 31, 2026
ee4ef6b
fw/apps/prf/mfg_charge: simplify charge test
gmarull Apr 1, 2026
24102d7
fw/apps/prf/mfg_menu: move aging test to main menu
gmarull Apr 1, 2026
18b4e94
fw/apps/prf: remove BT Device Name menu item and app
gmarull Apr 1, 2026
dbb43fd
fw/apps/prf/mfg_menu: show S/N and battery % in Device Info subtitle
gmarull Apr 1, 2026
8bdbd2e
fw/apps/prf: remove extras menu, move Load PRF to main menu
gmarull Apr 1, 2026
d82d6c3
fw/apps/prf/mfg_menu: swap Reset/Shutdown order
gmarull Apr 1, 2026
0f7582d
fw/apps/prf/mfg_adv: show MAC address QR code, no test result
gmarull Apr 1, 2026
881ffbc
fw/apps/prf/mfg_test_menu: move Program Color to last test
gmarull Apr 1, 2026
051bd5d
fw/apps/prf: report programmed color in test result and QR
gmarull Apr 1, 2026
e3e56a3
fw/apps/prf/mfg_test_aging: rewrite aging test
gmarull Apr 1, 2026
6544d50
fw/apps/prf: remove discharge test
gmarull Apr 1, 2026
c5a3cb6
fw/apps/prf: add semi-finished and finished test modes
gmarull Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 78 additions & 128 deletions src/fw/apps/prf/mfg_accel.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,36 @@

#include "applib/app.h"
#include "applib/tick_timer_service.h"
#include "util/trig.h"
#include "applib/ui/app_window_stack.h"
#include "applib/ui/window.h"
#include "applib/ui/window_private.h"
#include "applib/ui/path_layer.h"
#include "applib/ui/text_layer.h"
#include "apps/prf/mfg_test_result.h"
#include "kernel/pbl_malloc.h"
#include "kernel/util/sleep.h"
#include "drivers/accel.h"
#include "drivers/rtc.h"
#include "process_state/app_state/app_state.h"
#include "process_management/pebble_process_md.h"
#include "services/common/evented_timer.h"
#include "util/bitset.h"
#include "util/size.h"
#include "system/logging.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#define STATUS_STRING_LEN 200

#define RANGE_MIN -1070
#define RANGE_MAX -930
#define PREPARE_TIME_MS 3000
#define SAMPLE_TIME_MS 5000
// Minimum variation required on each axis (in mG)
#define MIN_VARIATION_MG 500

#define TEST_DURATION_MS 5000
#define RESULT_DISPLAY_MS 1000
#define SAMPLE_INTERVAL_MS 100

typedef enum {
STATE_IDLE,
STATE_PREPARE_FLAT,
STATE_MEASURE_FLAT,
STATE_RESULT_FLAT,
STATE_PREPARE_LEFT,
STATE_MEASURE_LEFT,
STATE_RESULT_LEFT,
STATE_PREPARE_FRONT,
STATE_MEASURE_FRONT,
STATE_RESULT_FRONT,
STATE_TESTING,
STATE_RESULT,
} TestState;

static EventedTimerID s_timer;
Expand All @@ -55,13 +46,21 @@ typedef struct {

TestState state;
RtcTicks state_start_time;
int32_t sum;
int32_t avg;
uint8_t cnt;
bool pass;
} AppData;

static void prv_start_test(AppData *data);
// Track min/max per axis
int16_t min_x, max_x;
int16_t min_y, max_y;
int16_t min_z, max_z;

uint32_t sample_count;

// Variation per axis
int16_t variation_x;
int16_t variation_y;
int16_t variation_z;

bool test_passed;
} AppData;

static void prv_update_display(void *context) {
AppData *data = context;
Expand All @@ -76,108 +75,76 @@ static void prv_update_display(void *context) {
return;
}

PBL_LOG_DBG("Accel (mG): X:%" PRIi16 " Y:%" PRIi16 " Z:%" PRIi16,
sample.x, sample.y, sample.z);

uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->state_start_time);

switch (data->state) {
case STATE_IDLE:
case STATE_IDLE: {
sniprintf(data->status_string, sizeof(data->status_string),
"X: %"PRIi16"\nY: %"PRIi16"\nZ: %"PRIi16"\n\nPress SEL to start test",
"X: %" PRIi16 " mG\nY: %" PRIi16 " mG\nZ: %" PRIi16 " mG\n\nPress SEL",
sample.x, sample.y, sample.z);
break;

case STATE_PREPARE_FLAT:
sniprintf(data->status_string, sizeof(data->status_string),
"Place FLAT\n\nStarting in %"PRIu32" sec",
(PREPARE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= PREPARE_TIME_MS) {
data->state = STATE_MEASURE_FLAT;
data->state_start_time = rtc_get_ticks();
data->sum = 0;
data->cnt = 0;
}
break;

case STATE_MEASURE_FLAT:
data->sum += sample.z;
data->cnt++;
sniprintf(data->status_string, sizeof(data->status_string),
"Measuring FLAT\n\nZ: %"PRIi16"\n%"PRIu32" sec remaining",
sample.z, (SAMPLE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= SAMPLE_TIME_MS) {
data->avg = data->sum / (int32_t)data->cnt;
data->pass = (data->avg >= RANGE_MIN && data->avg <= RANGE_MAX);
data->state = STATE_RESULT_FLAT;
}
break;

case STATE_RESULT_FLAT: {
sniprintf(data->status_string, sizeof(data->status_string),
"FLAT: %s\n\nZ avg: %"PRId32"\nExpected: %d to %d\n\nPress SEL",
data->pass ? "PASS" : "FAIL", data->avg, RANGE_MIN, RANGE_MAX);
break;
}

case STATE_PREPARE_LEFT:
sniprintf(data->status_string, sizeof(data->status_string),
"Turn LEFT\n\nStarting in %"PRIu32" sec",
(PREPARE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= PREPARE_TIME_MS) {
data->state = STATE_MEASURE_LEFT;
data->state_start_time = rtc_get_ticks();
data->sum = 0;
data->cnt = 0;
case STATE_TESTING: {
// Track min/max for each axis
if (data->sample_count == 0) {
data->min_x = data->max_x = sample.x;
data->min_y = data->max_y = sample.y;
data->min_z = data->max_z = sample.z;
} else {
if (sample.x < data->min_x) data->min_x = sample.x;
if (sample.x > data->max_x) data->max_x = sample.x;
if (sample.y < data->min_y) data->min_y = sample.y;
if (sample.y > data->max_y) data->max_y = sample.y;
if (sample.z < data->min_z) data->min_z = sample.z;
if (sample.z > data->max_z) data->max_z = sample.z;
}
break;
data->sample_count++;

case STATE_MEASURE_LEFT:
data->sum += sample.x;
data->cnt++;
sniprintf(data->status_string, sizeof(data->status_string),
"Measuring LEFT\n\nX: %"PRIi16"\n%"PRIu32" sec remaining",
sample.x, (SAMPLE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= SAMPLE_TIME_MS) {
data->avg = data->sum / (int32_t)data->cnt;
data->pass = (data->avg >= RANGE_MIN && data->avg <= RANGE_MAX);
data->state = STATE_RESULT_LEFT;
}
break;
// Calculate current variations
int16_t curr_var_x = data->max_x - data->min_x;
int16_t curr_var_y = data->max_y - data->min_y;
int16_t curr_var_z = data->max_z - data->min_z;

case STATE_RESULT_LEFT: {
sniprintf(data->status_string, sizeof(data->status_string),
"LEFT: %s\n\nX avg: %"PRId32"\nExpected: %d to %d\n\nPress SEL",
data->pass ? "PASS" : "FAIL", data->avg, RANGE_MIN, RANGE_MAX);
break;
}

case STATE_PREPARE_FRONT:
sniprintf(data->status_string, sizeof(data->status_string),
"Turn FRONT\n\nStarting in %"PRIu32" sec",
(PREPARE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= PREPARE_TIME_MS) {
data->state = STATE_MEASURE_FRONT;
"Testing...\nRotate device\n\nX: %" PRId16 " mG\nY: %" PRId16
" mG\nZ: %" PRId16 " mG\n\n%" PRIu32 " sec remaining",
curr_var_x, curr_var_y, curr_var_z,
(TEST_DURATION_MS - elapsed) / 1000 + 1);

if (elapsed >= TEST_DURATION_MS) {
// Store final variations
data->variation_x = curr_var_x;
data->variation_y = curr_var_y;
data->variation_z = curr_var_z;

// Test passes if all axes vary by at least the minimum threshold
data->test_passed =
(data->variation_x >= MIN_VARIATION_MG &&
data->variation_y >= MIN_VARIATION_MG &&
data->variation_z >= MIN_VARIATION_MG);

mfg_test_result_report(MfgTestId_Accel, data->test_passed, 0);

data->state = STATE_RESULT;
data->state_start_time = rtc_get_ticks();
data->sum = 0;
data->cnt = 0;
}
break;
}

case STATE_MEASURE_FRONT:
data->sum += sample.y;
data->cnt++;
sniprintf(data->status_string, sizeof(data->status_string),
"Measuring FRONT\n\nY: %"PRIi16"\n%"PRIu32" sec remaining",
sample.y, (SAMPLE_TIME_MS - elapsed) / 1000 + 1);
if (elapsed >= SAMPLE_TIME_MS) {
data->avg = data->sum / (int32_t)data->cnt;
data->pass = (data->avg >= RANGE_MIN && data->avg <= RANGE_MAX);
data->state = STATE_RESULT_FRONT;
case STATE_RESULT: {
if (elapsed >= RESULT_DISPLAY_MS) {
app_window_stack_pop(false);
return;
}
break;

case STATE_RESULT_FRONT: {
sniprintf(data->status_string, sizeof(data->status_string),
"FRONT: %s\n\nY avg: %"PRId32"\nExpected: %d to %d\n\nPress SEL",
data->pass ? "PASS" : "FAIL", data->avg, RANGE_MIN, RANGE_MAX);
"ACCEL: %s\n\nX: %" PRId16 " mG\nY: %" PRId16
" mG\nZ: %" PRId16 " mG",
data->test_passed ? "PASS" : "FAIL",
data->variation_x, data->variation_y, data->variation_z);
break;
}
}
Expand All @@ -190,22 +157,10 @@ static void prv_select_click_handler(ClickRecognizerRef recognizer, void *contex

switch (data->state) {
case STATE_IDLE:
prv_start_test(data);
break;

case STATE_RESULT_FLAT:
data->state = STATE_PREPARE_LEFT;
data->state_start_time = rtc_get_ticks();
break;

case STATE_RESULT_LEFT:
data->state = STATE_PREPARE_FRONT;
data->state_start_time = rtc_get_ticks();
break;

case STATE_RESULT_FRONT:
data->state = STATE_IDLE;
// Start test
data->state = STATE_TESTING;
data->state_start_time = rtc_get_ticks();
data->sample_count = 0;
break;

default:
Expand All @@ -217,11 +172,6 @@ static void prv_click_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler);
}

static void prv_start_test(AppData *data) {
data->state = STATE_PREPARE_FLAT;
data->state_start_time = rtc_get_ticks();
}

static void prv_handle_init(void) {
AppData *data = app_malloc_check(sizeof(AppData));
*data = (AppData) {};
Expand Down
54 changes: 26 additions & 28 deletions src/fw/apps/prf/mfg_bt_device_name.c → src/fw/apps/prf/mfg_adv.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,22 @@
#include "applib/app.h"
#include "applib/ui/app_window_stack.h"
#include "applib/ui/qr_code.h"
#include "applib/ui/text_layer.h"
#include "applib/ui/window.h"
#include "applib/ui/window_private.h"
#include "applib/ui/text_layer.h"
#include "kernel/pbl_malloc.h"
#include "process_state/app_state/app_state.h"
#include "process_management/pebble_process_md.h"
#include "process_state/app_state/app_state.h"
#include "services/common/bluetooth/bluetooth_ctl.h"
#include "services/common/bluetooth/local_id.h"
#include "util/bitset.h"
#include "util/size.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct {
Window window;

QRCode qr_code;
TextLayer name;

char name_buffer[BT_DEVICE_NAME_BUFFER_SIZE];
TextLayer mac_label;
char mac_buffer[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE];
} AppData;

static void prv_handle_init(void) {
Expand All @@ -33,12 +28,12 @@ static void prv_handle_init(void) {
app_state_set_user_data(data);

Window *window = &data->window;
window_init(window, "");
window_init(window, "BLE Adv");
window_set_fullscreen(window, true);

bt_local_id_copy_device_name(data->name_buffer, false);
bt_local_id_copy_address_mac_string(data->mac_buffer);

QRCode* qr_code = &data->qr_code;
QRCode *qr_code = &data->qr_code;
qr_code_init_with_parameters(qr_code,
#if PBL_ROUND
#define QR_CODE_SIZE ((window->layer.bounds.size.w * 10) / 14)
Expand All @@ -49,36 +44,39 @@ static void prv_handle_init(void) {
&GRect(10, 10, window->layer.bounds.size.w - 20,
window->layer.bounds.size.h - 30),
#endif
data->name_buffer, strlen(data->name_buffer), QRCodeECCMedium,
data->mac_buffer, strlen(data->mac_buffer), QRCodeECCMedium,
GColorBlack, GColorWhite);
layer_add_child(&window->layer, &qr_code->layer);

TextLayer* name = &data->name;
text_layer_init_with_parameters(name,
TextLayer *mac_label = &data->mac_label;
text_layer_init_with_parameters(mac_label,
&GRect(0, window->layer.bounds.size.h - PBL_IF_RECT_ELSE(20, 40),
window->layer.bounds.size.w, 20),
data->name_buffer, fonts_get_system_font(FONT_KEY_GOTHIC_14),
data->mac_buffer, fonts_get_system_font(FONT_KEY_GOTHIC_14),
GColorBlack, GColorWhite, GTextAlignmentCenter,
GTextOverflowModeTrailingEllipsis);
layer_add_child(&window->layer, &name->layer);
layer_add_child(&window->layer, &mac_label->layer);

app_window_stack_push(window, true /* Animated */);
app_window_stack_push(window, true);
}

static void s_main(void) {
// Restart BLE so it begins advertising
bt_ctl_set_enabled(false);
bt_ctl_set_enabled(true);

prv_handle_init();

app_event_loop();
}

const PebbleProcessMd* mfg_bt_device_name_app_get_info(void) {
const PebbleProcessMd *mfg_adv_app_get_info(void) {
static const PebbleProcessMdSystem s_app_info = {
.common.main_func = &s_main,
// UUID: 31b5a232-d638-4ccb-b89a-910202d85a1f
.common.uuid = { 0x31, 0xb5, 0xa2, 0x32, 0xd6, 0x38, 0x4c, 0xcb,
0xb8, 0x9a, 0x91, 0x02, 0x02, 0xd8, 0x5a, 0x1f },
.name = "MfgBTDeviceName",
.common.main_func = &s_main,
// UUID: 4c8e2a1f-7d3b-4f9e-b5a2-6e1c8d3f7a9b
.common.uuid = {0x4c, 0x8e, 0x2a, 0x1f, 0x7d, 0x3b, 0x4f, 0x9e,
0xb5, 0xa2, 0x6e, 0x1c, 0x8d, 0x3f, 0x7a, 0x9b},
.name = "MfgAdv",
};
return (const PebbleProcessMd*) &s_app_info;
return (const PebbleProcessMd *)&s_app_info;
}

Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/* SPDX-FileCopyrightText: 2025 Core Devices LLC */
/* SPDX-License-Identifier: Apache-2.0 */


#pragma once

#include "process_management/pebble_process_md.h"

const PebbleProcessMd* mfg_bt_device_name_app_get_info(void);

const PebbleProcessMd *mfg_adv_app_get_info(void);
Loading
Loading