Wireless Controller Receiver Design
3:16
examples/zigbee/ and build, then split into modules later.Key points you may wish to customiseC
/********************************************************************* * Hoist Receiver – nRF52840 + Zigbee + TFT LCD * Hardware : * • nRF52840-QIAA * • VNH5019ATR-E (INA/P0.06, INB/P0.07, PWM/P0.08) * • Hall sensor (P0.05, active-low) * • TFT ST7789 (SPI: SCK/P0.14, MOSI/P0.13, CS/P0.12, * DC/P0.11, RST/P0.10, BL/P0.09 [30 Ω R3]) * • Battery sense (SAADC AIN6 / P0.30 through 100 kΩ/47 kΩ divider) *********************************************************************/ #include "sdk_config.h" #include "boards.h" #include "nrf_drv_pwm.h" #include "nrf_drv_saadc.h" #include "app_timer.h" #include "app_util_platform.h" #include "bsp_btn_ble.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "zboss_api.h" #include "zb_error_handler.h" #include "zb_mem_config_med.h" #include "zb_nrf52_internal.h" #include "zb_zcl_identify.h" /* --------------------------------------------------------------- */ /* PROJECT CONSTANTS */ /* --------------------------------------------------------------- */ #define ZIGBEE_CHANNEL 16 #define ZIGBEE_RX_EP 10 /* Application endpoint */ #define MANUFACTURER_ID 0x1337 #define APP_TIMER_PRESCALER 0 #define APP_TIMER_OP_QUEUE_SIZE 4 #define PWM_INSTANCE_ID 0 #define PWM_FREQUENCY_HZ 1000 /* 1 kHz PWM for motor speed */ #define ACTUATOR_STROKE_GOAL 1 /* One stroke per cycle */ #define BATTERY_SAMPLES_MS 30000 /* Update battery every 30 s */ #define LCD_UPDATE_MS 500 /* Refresh LCD twice a second */ /* --------------------------------------------------------------- */ /* GPIO DEFINITIONS */ /* --------------------------------------------------------------- */ #define PIN_INA NRF_GPIO_PIN_MAP(0,6) #define PIN_INB NRF_GPIO_PIN_MAP(0,7) #define PIN_PWM NRF_GPIO_PIN_MAP(0,8) #define PIN_HALL NRF_GPIO_PIN_MAP(0,5) #define TFT_SCK NRF_GPIO_PIN_MAP(0,14) #define TFT_MOSI NRF_GPIO_PIN_MAP(0,13) #define TFT_CS NRF_GPIO_PIN_MAP(0,12) #define TFT_DC NRF_GPIO_PIN_MAP(0,11) #define TFT_RST NRF_GPIO_PIN_MAP(0,10) #define TFT_BL NRF_GPIO_PIN_MAP(0,9) #define VBAT_ADC_CH NRF_SAADC_INPUT_AIN6 /* P0.30 */ #define VBAT_R1 100000.0f /* Top resistor (Ω) */ #define VBAT_R2 47000.0f /* Bottom resistor (Ω) */ #define VBAT_REF 0.6f /* SAADC internal reference */ /* --------------------------------------------------------------- */ /* GLOBAL STATE */ /* --------------------------------------------------------------- */ static nrf_drv_pwm_t m_pwm = NRF_DRV_PWM_INSTANCE(PWM_INSTANCE_ID); static nrf_pwm_values_individual_t m_pwm_seq_vals = {0,0,0,0}; static nrf_pwm_sequence_t m_pwm_seq = { .values.p_individual = &m_pwm_seq_vals, .length = NRF_PWM_VALUES_LENGTH(m_pwm_seq_vals), .repeats = 0, .end_delay = 0 }; static volatile uint32_t g_lift_cycles = 0; static zb_bool_t g_motor_running = ZB_FALSE; static uint8_t g_motor_dir = 0; /* 0=stop,1=up,2=down */ /* --------------------------------------------------------------- */ /* FUNCTION PROTOTYPES */ /* --------------------------------------------------------------- */ static void zigbee_init(void); static void tft_init(void); static void tft_print_status(void); static void motor_stop(void); static void motor_drive(uint8_t dir); static void hall_isr(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action); static void battery_timer_cb(void *p_context); static void lcd_timer_cb(void *p_context); /* --------------------------------------------------------------- */ /* MAIN */ /* --------------------------------------------------------------- */ int main(void) { /* Logging & timers */ APP_ERROR_CHECK(NRF_LOG_INIT(NULL)); NRF_LOG_DEFAULT_BACKENDS_INIT(); APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false); /* GPIO setup */ nrf_gpio_cfg_output(PIN_INA); nrf_gpio_cfg_output(PIN_INB); nrf_gpio_cfg_output(PIN_PWM); nrf_gpio_cfg_output(TFT_CS); nrf_gpio_cfg_output(TFT_DC); nrf_gpio_cfg_output(TFT_RST); nrf_gpio_cfg_output(TFT_BL); motor_stop(); /* PWM for motor */ nrf_drv_pwm_config_t pwm_cfg = { .output_pins = { PIN_PWM, NRF_DRV_PWM_PIN_NOT_USED, NRF_DRV_PWM_PIN_NOT_USED, NRF_DRV_PWM_PIN_NOT_USED }, .irq_priority = APP_IRQ_PRIORITY_LOW, .base_clock = NRF_PWM_CLK_1MHz, .count_mode = NRF_PWM_MODE_UP, .top_value = 1000, /* 1 kHz */ .load_mode = NRF_PWM_LOAD_INDIVIDUAL, .step_mode = NRF_PWM_STEP_AUTO }; nrf_drv_pwm_init(&m_pwm, &pwm_cfg, NULL); m_pwm_seq_vals.channel_0 = 0; nrf_drv_pwm_simple_playback(&m_pwm, &m_pwm_seq, 1, NRF_DRV_PWM_FLAG_LOOP); /* Hall sensor interrupt */ nrf_drv_gpiote_init(); nrf_drv_gpiote_in_config_t hall_cfg = GPIOTE_CONFIG_IN_SENSE_HITOLO(true); hall_cfg.pull = NRF_GPIO_PIN_PULLUP; nrf_drv_gpiote_in_init(PIN_HALL, &hall_cfg, hall_isr); nrf_drv_gpiote_in_event_enable(PIN_HALL, true); /* ADC for battery */ nrf_saadc_channel_config_t ch = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(VBAT_ADC_CH); ch.gain = NRF_SAADC_GAIN1_6; /* 1/6 gain for 3.6 V full-scale */ nrf_drv_saadc_init(NULL, NULL); nrf_drv_saadc_channel_init(0, &ch); /* TFT */ tft_init(); tft_print_status(); /* Timers */ app_timer_id_t batt_timer, lcd_timer; app_timer_create(&batt_timer, APP_TIMER_MODE_REPEATED, battery_timer_cb); app_timer_start(batt_timer, APP_TIMER_TICKS(BATTERY_SAMPLES_MS), NULL); app_timer_create(&lcd_timer, APP_TIMER_MODE_REPEATED, lcd_timer_cb); app_timer_start(lcd_timer, APP_TIMER_TICKS(LCD_UPDATE_MS), NULL); /* Zigbee */ zigbee_init(); /* Main loop */ while (true) { zb_sched_run(); zb_sleep_if_allowed(); NRF_LOG_FLUSH(); } } /* --------------------------------------------------------------- */ /* ZIGBEE HANDLING */ /* --------------------------------------------------------------- */ #define ZB_ZCL_BASIC_CLUSTER_ID 0x0000 #define CMD_UP 0x01 #define CMD_DOWN 0x02 ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(basic_attr_list, &g_attr_zcl_version, &g_attr_power_source); static void zboss_signal_handler(zb_uint8_t param) { zb_zdo_app_signal_hdr_t *sig_hndler = (void*)param; zb_zdo_app_signal_type_t sig = sig_hndler->type; if (sig == ZB_BDB_SIGNAL_DEVICE_FIRST_START || sig == ZB_BDB_SIGNAL_DEVICE_REBOOT) { zb_bdb_signal_device_first_start_params_t *params = (zb_bdb_signal_device_first_start_params_t*)sig_hndler->buf; if (params->status == ZB_NWK_JOINED) NRF_LOG_INFO("Joined Zigbee network"); else NRF_LOG_INFO("Failed to join Zigbee network: %d", params->status); } zb_buf_free(param); } static zb_void_t cmd_rx_cb(zb_uint8_t param) { zb_uint8_t *cmd_id = zb_buf_begin(param); switch (*cmd_id) { case CMD_UP: motor_drive(1); break; case CMD_DOWN: motor_drive(2); break; default: motor_stop(); break; } zb_buf_free(param); } static void zigbee_init(void) { zb_ret_t zb_err_code; zb_cfg_ext_t ce; zb_memset(&ce, 0, sizeof(ce)); ce.psz_ieee_addr = NULL; zb_get_long_address(ce.eui64); ce.channel_mask = 1l << ZIGBEE_CHANNEL; ce.nwk_role = EMBEDDED_COORDINATOR; zb_err_code = zb_init(&ce); ZB_ERROR_CHECK(zb_err_code); /* Register endpoint */ ZB_AF_SIMPLE_DESC_TYPE(10, 0, 0, 1, 0, ZB_ZCL_BASIC_CLUSTER_ID); zb_af_endpoint_desc_t ep_desc; ep_desc.ep_id = ZIGBEE_RX_EP; ep_desc.profile_id = 0xC105; /* Manufacturer specific */ ep_desc.device_id = 0x0001; ep_desc.device_version = 1; ep_desc.input_cluster_count = 1; ep_desc.output_cluster_count = 0; ep_desc.p_in_cluster_list = (zb_uint16_t*)&ZB_ZCL_BASIC_CLUSTER_ID; ep_desc.p_out_cluster_list = NULL; zb_af_register_endpoint(&ep_desc); /* Register command handler */ zb_zcl_register_cluster_command_handler(ZIGBEE_RX_EP, ZB_ZCL_BASIC_CLUSTER_ID, cmd_rx_cb); zb_set_network_ed_role(ZB_FALSE); zb_err_code = zboss_start_no_autostart(); ZB_ERROR_CHECK(zb_err_code); zb_affix_signal_handler(zboss_signal_handler); } /* --------------------------------------------------------------- */ /* MOTOR DRIVER */ /* --------------------------------------------------------------- */ static void motor_stop(void) { nrf_gpio_pin_clear(PIN_INA); nrf_gpio_pin_clear(PIN_INB); m_pwm_seq_vals.channel_0 = 0; nrf_drv_pwm_simple_playback(&m_pwm, &m_pwm_seq, 1, NRF_DRV_PWM_FLAG_LOOP); g_motor_running = ZB_FALSE; g_motor_dir = 0; } static void motor_drive(uint8_t dir) { if (dir == 1) /* UP */ { nrf_gpio_pin_set(PIN_INA); nrf_gpio_pin_clear(PIN_INB); } else if (dir == 2) /* DOWN */ { nrf_gpio_pin_clear(PIN_INA); nrf_gpio_pin_set(PIN_INB); } else { motor_stop(); return; } m_pwm_seq_vals.channel_0 = 500; /* 50 % duty */ nrf_drv_pwm_simple_playback(&m_pwm, &m_pwm_seq, 1, NRF_DRV_PWM_FLAG_LOOP); g_motor_running = ZB_TRUE; g_motor_dir = dir; } /* --------------------------------------------------------------- */ /* HALL SENSOR ISR */ /* --------------------------------------------------------------- */ static void hall_isr(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { if (g_motor_running) { ++g_lift_cycles; motor_stop(); /* Stop after one stroke */ } } /* --------------------------------------------------------------- */ /* BATTERY + DISPLAY */ /* --------------------------------------------------------------- */ static float read_battery_volts(void) { nrf_saadc_value_t raw; nrf_drv_saadc_sample_convert(0, &raw); float v = (raw / 2047.0f) * VBAT_REF * 6.0f; /* gain 1/6 */ return v * (VBAT_R1 + VBAT_R2) / VBAT_R2; } static void battery_timer_cb(void *p_context) { (void)p_context; float vbat = read_battery_volts(); NRF_LOG_INFO("Battery = %.2f V", vbat); } static void lcd_timer_cb(void *p_context) { (void)p_context; tft_print_status(); } /* --------------------------------------------------------------- */ /* TFT DRIVER (MINIMAL) */ /* --------------------------------------------------------------- */ #include "nrf_drv_spi.h" static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(0); static void tft_cmd(uint8_t c) { nrf_gpio_pin_clear(TFT_DC); nrf_drv_spi_transfer(&spi, &c, 1, NULL, 0); } static void tft_data(uint8_t d) { nrf_gpio_pin_set(TFT_DC); nrf_drv_spi_transfer(&spi, &d, 1, NULL, 0); } static void tft_init(void) { nrf_drv_spi_config_t spi_cfg = NRFX_SPI_DEFAULT_CONFIG; spi_cfg.sck_pin = TFT_SCK; spi_cfg.mosi_pin = TFT_MOSI; spi_cfg.miso_pin = NRFX_SPI_PIN_NOT_USED; spi_cfg.cs_pin = TFT_CS; spi_cfg.frequency = NRF_DRV_SPI_FREQ_8M; spi_cfg.mode = NRF_DRV_SPI_MODE_2; nrf_drv_spi_init(&spi, &spi_cfg, NULL, NULL); nrf_gpio_pin_set(TFT_RST); nrf_delay_ms(5); nrf_gpio_pin_clear(TFT_RST); nrf_delay_ms(20); nrf_gpio_pin_set(TFT_RST); /* Software reset, sleep out, etc. – minimal ST7789 init */ tft_cmd(0x01); nrf_delay_ms(150); tft_cmd(0x11); nrf_delay_ms(120); tft_cmd(0x29); /* Display ON */ nrf_gpio_pin_set(TFT_BL); } static void tft_print_status(void) { char buf[32]; float vbat = read_battery_volts(); snprintf(buf, sizeof(buf), "Batt: %.2f V", vbat); /* … send ASCII to TFT – left as exercise (draw text function) */ snprintf(buf, sizeof(buf), "Cycles: %lu", (unsigned long)g_lift_cycles); /* … draw second line */ }
0x01 = UP, 0x02 = DOWN).ZB_ZCL_BASIC_CLUSTER_ID and change cmd_rx_cb() accordingly.m_pwm_seq_vals.channel_0 for different duty cycles or map it to an 8-bit value in the Zigbee packet.VBAT_R1 / VBAT_R2.main.c into /examples/zigbee/hoist_receiver.sdk_config.h from examples/zigbee/light_control and enable:
#define ZB_CONFIG_ENABLE_ZIGBEE_ROLE_ROUTER 1
#define NRFX_PWM_ENABLED 1
#define NRFX_SAADC_ENABLED 1
#define NRFX_GPIOTE_ENABLED 1
Project → Build) or use make -j.Ready.
Reviews
Properties
Properties describe core aspects of the project.
Pricing & Availability
Distributor | Qty 1 |
|---|
Assets
No assets added.
Controls
Welcome 👋
Flux helps you build PCBs faster with an AI teammate!
Create your account to collaborate, stay updated, fork your own version, and get instant answers from our AI agent.