Intelligent kettle (Bluetooth version) 03 - realization of offline control function

preface

The common kettle generally has boiling and heat preservation functions. On this basis, this case uses the graffiti BLE SDK and BLE module to realize the remote control of the kettle, including the functions of heat preservation temperature setting, water quality mode selection, reservation of water boiling, etc. in addition, it also adds dry burning alarm and other functions to make the kettle more intelligent.

The full Demo is available in tuya-iotos-embeded-demo-ble-smart-kettle Get from.

Function setting

The intelligent kettle equipment in this case is provided with two buttons for boiling and heat preservation, three indicator lights and a buzzer for status prompt, which can be controlled locally or remotely. The specific function settings are as follows:

functionexplain
boilingGently touch the boiling key, the buzzer will give a "drip" reminder, and switch the boiling function on / off;
When the boiling function is turned on, the red light is on and the heating is turned on until the boiling temperature is reached;
When the boiling function is turned off or the boiling is completed, the red light goes out and the heating is turned off;
heat preservationTouch the insulation key, the buzzer will give a "drip" reminder, and switch the insulation function on / off;
When the insulation function is turned on and the insulation temperature is not reached, the orange light is on, the green light is off, and the heating is turned on or off according to the current temperature;
When the insulation function is turned off or reaches the insulation temperature, the orange light is off, the green light is on, and the heating is turned off;
When the water type is tap water, it shall be heated to boiling and then kept warm to the holding temperature;
When the water type is pure water, it shall be directly heated to the insulation temperature;
The insulation temperature is 55 ℃ by default, which can be set through APP, and the setting range is 45-90 ℃;
The water type is tap water by default, which can be set through APP;
Dry burning alarmWhen dry burning is detected, the heating will be automatically turned off, the hardware will be powered off, and the buzzer will sound for a long time;
Distribution networkPower on the distribution network, the distribution network is not successful after 3 minutes, and can only be controlled locally;
Long press the insulation key for 5 seconds to enter the distribution network state. After 3 minutes, the distribution network is not successful, and can only be controlled locally;
When waiting for the distribution network, the green light flashes quickly;
Remote controlThe items that can be operated through the APP include: turning on / off the boiling function and insulation function, setting the insulation temperature and water type, viewing the current temperature and fault status, and making an appointment for water boiling time;

Later, we will introduce the specific scheme and process to realize the above functions one by one.

Realization of off-line control function

(1) Indicator drive control

The indicator light is used as the kettle status indication and distribution network indication. The driving mode is level driving. The basic settings are as follows:

indicator lightPinHLdisplay mode
Red P_LED_REDP24/GPIO_PB5ExtinguishbrightFixed (normally on / off)
Orange P_LED_ORANGEP26/GPIO_PB4ExtinguishbrightFixed (normally on / off)
Green P_GREENP6/GPIO_PD2ExtinguishbrightFixed (normally on / off) / flashing (0.2s on and 0.2s off)

Code implementation:

#define P_LED_RED           GPIO_PB5    /* P24 */
#define P_LED_ORANGE        GPIO_PB4    /* P26 */
#define P_LED_GREEN         GPIO_PD2    /* P6 */
#define LED_TWINKLE_TIME	200			/* 0.2s */

LED_MODE_E g_led_green_mode = LED_MODE_FIX;
LED_MODE_E g_led_green_status = OFF;

/* LED Port initialization */
void led_init(void)
{
	gpio_set_func(P_LED_RED, AS_GPIO);
    gpio_set_output_en(P_LED_RED, 1);
    gpio_write(P_LED_RED, 1);
    gpio_set_func(P_LED_ORANGE, AS_GPIO);
    gpio_set_output_en(P_LED_ORANGE, 1);
    gpio_write(P_LED_ORANGE, 1);
    gpio_set_output_en(P_LED_GREEN, 1);
	gpio_set_func(P_LED_GREEN, AS_GPIO);
    gpio_write(P_LED_GREEN, 1);
}

/* Red LED on and off control */
void set_led_red(bool b_on_off)
{
    static bool s_last_status = 0;
    if (s_last_status != b_on_off) {
        if (b_on_off == ON) {
        	gpio_write(P_LED_RED, 0);
        } else {
        	gpio_write(P_LED_RED, 1);
        }
        s_last_status = b_on_off;
    }
}

/* Orange LED on and off control */
void set_led_orange(bool b_on_off)
{
    static bool s_last_status = 0;
    if (s_last_status != b_on_off) {
        if (b_on_off == ON) {
        	gpio_write(P_LED_ORANGE, 0);
        } else {
        	gpio_write(P_LED_ORANGE, 1);
        }
        s_last_status = b_on_off;
    }
}

/* Green LED on and off control */
void set_led_green(bool b_on_off)
{
    static bool s_last_status = 0;

    if (s_last_status != b_on_off) {
        if (b_on_off == ON) {
        	gpio_write(P_LED_GREEN, 0);
        } else {
        	gpio_write(P_LED_GREEN, 1);
        }
        s_last_status = b_on_off;
    }
}

/* Green LED mode setting */
void set_led_green_mode(LED_MODE_E mode)
{
    g_led_green_mode = mode;
}

/* Green LED status setting */
void set_led_green_status(uint8_t status)
{
    g_led_green_status = status;
}

/* Green LED status update */
void update_led_green_status(void)
{
    static bool s_status = 0;
    static uint32_t s_twinkle_tm = 0;

    switch (g_led_green_mode) {
    case LED_MODE_FIX:      /* Fixed mode: control the LED on and off according to the status setting */
        set_led_green(g_led_green_status);
        s_twinkle_tm = 0;
        break;
    case LED_MODE_TWINKLE:  /* Flashing mode: turn on and off once for 0.2s */
        if (!clock_time_exceed(s_twinkle_tm, LED_TWINKLE_TIME*1000)) {
            break;
        }
        s_twinkle_tm = clock_time();
        s_status = !s_status;
        set_led_green(s_status);
        break;
    default:
        break;
    }
}

(2) Relay drive control

The on-off of the relay is used to control the heating circuit. The driving form is level driving. The basic settings are as follows:

relayPinHL
Relay P_RELAYP14/GPIO_PD3Turn on heatingTurn off heating

Code implementation:

#define P_RELAY	GPIO_PD3    /* P14 */

/* Relay port initialization */
void relay_init(void)
{
	gpio_set_func(P_RELAY, AS_GPIO);
	gpio_set_output_en(P_RELAY, 1);
	gpio_write(P_RELAY, 0);
}

/* Relay on-off control */
void set_relay(bool b_on_off)
{
    static bool s_last_status = 0;
    if (s_last_status != b_on_off) {
        if (b_on_off == ON) {
        	gpio_write(P_RELAY, 1);
        } else {
        	gpio_write(P_RELAY, 0);
        }
        s_last_status = b_on_off;
    }
}

(3) Buzzer drive control

The buzzer is used as key prompt tone and fault alarm prompt tone. The driving mode is square wave signal driving. The basic settings are as follows:

BuzzerPinpatternSquare wave signal (PWM)
Buzzer P_BUZZERP17/GPIO_PD4Stop / beep (70ms) / fault (long Beep)Channel: PWM2
Cycle: 500us (2KHz)
Duty cycle: 50%

Code implementation:

#define P_BUZZER            GPIO_PD4    /* P17 */
#define PWM_ID_BUZZER       PWM2_ID		/* PWM2 */
#define BUZZER_ONCE_TIME    70			/* 70ms */

typedef BYTE_T BUZZER_MODE_E; 
#define BUZZER_MODE_STOP    0x00 		/*  Stop*/
#define BUZZER_MODE_ONCE    0x01 		/*  A drop*/
#define BUZZER_MODE_FAULT   0x02 		/*  Malfunction*/

static uint8_t sg_buzzer_timer = OFF;
static uint32_t sg_buzzer_tm = 0;

/* Buzzer port and PWM configuration initialization */
void buzzer_pwm_init(void)
{
    pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
	gpio_set_func(P_BUZZER, AS_PWM2_N);
    pwm_set_mode(PWM_ID_BUZZER, PWM_NORMAL_MODE);
    pwm_set_cycle_and_duty(PWM_ID_BUZZER, (uint16_t)(500 * CLOCK_SYS_CLOCK_1US), (uint16_t)(250 * CLOCK_SYS_CLOCK_1US));    /* 500us 2KHz 50% */
    gpio_write(P_BUZZER, 0);
}

/* Buzzer switch control */
static void set_buzzer(bool b_on_off)
{
    static bool s_last_status = 0;
    if (s_last_status != b_on_off) {
        if (b_on_off == ON) {
        	pwm_start(PWM_ID_BUZZER);
        } else {
        	pwm_stop(PWM_ID_BUZZER);
            gpio_write(P_BUZZER, 0);
        }
        s_last_status = b_on_off;
    }
}

/* Buzzer mode control */
void set_buzzer_mode(BUZZER_MODE_E mode)
{
    switch (mode) {
    case BUZZER_MODE_STOP:
        set_buzzer(OFF);
        break;
    case BUZZER_MODE_ONCE:
        set_buzzer(ON);
        sg_buzzer_timer = ON;
        sg_buzzer_tm = clock_time();
        break;
    case BUZZER_MODE_FAULT:
        set_buzzer(ON);
        break;
    default:
        break;
    }
}

/* Buzzer status update */
void update_buzzer_status(void)
{
    if (sg_buzzer_timer == ON) {
        if (!clock_time_exceed(sg_buzzer_tm, BUZZER_ONCE_TIME*1000)) {
            return;
        }
        set_buzzer(OFF);
        sg_buzzer_timer = OFF;
    }
}

(4) Key detection and processing

For the later program expansion, the key detection and processing are realized by registering callback functions.

Code implementation:

a. Key registration information and initialization

The user can set the registration content as follows:

typedef void(* KEY_CALLBACK)();
typedef struct {
    uint16_t key1_pin;                  /* I/O port for key 1 */
    uint16_t key2_pin;                  /* I/O port for key 2 */
    KEY_CALLBACK key1_short_press_cb;   /* Callback function triggered by short press of key 1 */
    KEY_CALLBACK key2_short_press_cb;   /* Callback function triggered by short press of key 2 */
    KEY_CALLBACK key1_long_press_cb;    /* Press and hold key 1 to trigger the callback function */
    KEY_CALLBACK key2_long_press_cb;    /* Press and hold key 2 to trigger the callback function */
    uint32_t key1_long_press_time;      /* Key 1 long press time setting (ms) */
    uint32_t key2_long_press_time;      /* Key 2 long press time setting (ms) */
    uint32_t scan_time;                 /* Key 1 scan interval setting (ms) */
} TS02N_KEY_DEF_T;

Next, define the state variables required for key detection, manage them together with the user registration content, and then write the key initialization function:

/* Key status */
typedef struct {
    uint8_t cur_code;
    uint8_t prv_code;
    uint32_t cur_time;
    uint32_t prv_time;
} TS02N_KEY_STATUS_T;

/* Key management */
typedef struct {
    TS02N_KEY_DEF_T* ts02n_key_def_s;
    TS02N_KEY_STATUS_T ts02n_key_status_s;
} TS02N_KEY_MANAGE_T;
static TS02N_KEY_MANAGE_T *sg_key_mag = NULL; 

/* Key initialization */
uint8_t ts02n_key_init(TS02N_KEY_DEF_T* key_def)
{
    /* Key information initialization */
    sg_key_mag = (TS02N_KEY_MANAGE_T *)tuya_ble_malloc(sizeof(TS02N_KEY_MANAGE_T));
    memset(sg_key_mag, 0, sizeof(TS02N_KEY_MANAGE_T));
    sg_key_mag->ts02n_key_def_s = key_def;
    /* Callback function check */
    if ((key_def->key1_short_press_cb == NULL) &&
    	(key_def->key2_short_press_cb == NULL) &&
        (key_def->key1_long_press_cb == NULL)  &&
        (key_def->key2_long_press_cb == NULL)) {
        tuya_ble_free((uint8_t *)sg_key_mag);
        return KEY_INIT_ERR;
    }

    /* Port initialization */
    gpio_set_func(key_def->key1_pin, AS_GPIO);
    gpio_set_input_en(key_def->key1_pin, 1);
    gpio_setup_up_down_resistor(key_def->key1_pin, PM_PIN_PULLUP_10K);
    gpio_set_func(key_def->key2_pin, AS_GPIO);
    gpio_set_input_en(key_def->key2_pin, 1);
    gpio_setup_up_down_resistor(key_def->key2_pin, PM_PIN_PULLUP_10K);

    return KEY_INIT_OK;
}

b. Key scanning and judgment processing

First, define the key values of key 1 and key 2 and the short press confirmation time respectively:

#define KEY1_CODE				0x01
#define KEY2_CODE				0x02
#define KEY_PRESS_SHORT_TIME    50

Then write the key state scanning function and key processing function of timing cycle:

/* Get current key value */
static uint8_t get_key_code(void)
{
    uint8_t key_code = 0;
    /* Key 1 */
    if (gpio_read(sg_key_mag->ts02n_key_def_s->key1_pin) == 0) {
        key_code |= KEY1_CODE;
    } else {
        key_code &= ~KEY1_CODE;
    }
    /* Key 2 */
    if (gpio_read(sg_key_mag->ts02n_key_def_s->key2_pin) == 0) {
        key_code |= KEY2_CODE;
    } else {
        key_code &= ~KEY2_CODE;
    }
    return key_code;
}

/* Scan key status */
static void update_key_status(uint32_t time_inc)
{
    uint8_t key_code;

    key_code = get_key_code();
    sg_key_mag->ts02n_key_status_s.prv_time = sg_key_mag->ts02n_key_status_s.cur_time;
    sg_key_mag->ts02n_key_status_s.cur_time += time_inc;

    if (key_code != sg_key_mag->ts02n_key_status_s.cur_code) {
        sg_key_mag->ts02n_key_status_s.prv_code = sg_key_mag->ts02n_key_status_s.cur_code;
        sg_key_mag->ts02n_key_status_s.cur_code = key_code;
        sg_key_mag->ts02n_key_status_s.prv_time = sg_key_mag->ts02n_key_status_s.cur_time;
        sg_key_mag->ts02n_key_status_s.cur_time = 0;
    } else {
        sg_key_mag->ts02n_key_status_s.prv_code = sg_key_mag->ts02n_key_status_s.cur_code;
    }
}

/* Judge whether the [key_code] key press time exceeds [press_time] */
static uint8_t is_key_press_over_time(uint8_t key_code, uint32_t press_time)
{
    if (sg_key_mag->ts02n_key_status_s.cur_code == key_code) {
        if ((sg_key_mag->ts02n_key_status_s.cur_time >= press_time) &&
            (sg_key_mag->ts02n_key_status_s.prv_time < press_time)) {
            return 1;
        }
    }
    return 0;
}

/* Judge whether the pressing time when the [key_code] key is released is less than [press_time] */
static uint8_t is_key_release_to_release_less_time(uint8_t key_code, uint32_t press_time)
{
    if ((sg_key_mag->ts02n_key_status_s.prv_code == key_code) &&
        (sg_key_mag->ts02n_key_status_s.cur_code != key_code)) {
        if ((sg_key_mag->ts02n_key_status_s.prv_time >= KEY_PRESS_SHORT_TIME) &&
            (sg_key_mag->ts02n_key_status_s.prv_time < press_time)) {
            return 1;
        }
    }
    return 0;
}

/* Detect and handle key events */
static void detect_and_handle_key_event(void)
{
    /* Key 1 processing */
    if (sg_key_mag->ts02n_key_def_s->key1_long_press_cb != NULL) {
        /* Long press */
        if (is_key_press_over_time(KEY1_CODE, sg_key_mag->ts02n_key_def_s->key1_long_press_time)) {
            sg_key_mag->ts02n_key_def_s->key1_long_press_cb();
            TUYA_APP_LOG_DEBUG("key1 is long pressed");
        }
        /* Short press */
        if (sg_key_mag->ts02n_key_def_s->key1_short_press_cb != NULL) {
            if (is_key_release_to_release_less_time(KEY1_CODE, sg_key_mag->ts02n_key_def_s->key1_long_press_time)) {
                sg_key_mag->ts02n_key_def_s->key1_short_press_cb();
                TUYA_APP_LOG_DEBUG("key1 is pressed");
            }
        }
    } else {
        /* Short press */
        if (sg_key_mag->ts02n_key_def_s->key1_short_press_cb != NULL) {
            if (is_key_press_over_time(KEY1_CODE, KEY_PRESS_SHORT_TIME)) {
                sg_key_mag->ts02n_key_def_s->key1_short_press_cb();
                TUYA_APP_LOG_DEBUG("key1 is pressed");
            }
        }
    }
    /* Key 2 processing */
    if (sg_key_mag->ts02n_key_def_s->key2_long_press_cb != NULL) {
        /* Long press */
        if (is_key_press_over_time(KEY2_CODE, sg_key_mag->ts02n_key_def_s->key2_long_press_time)) {
            sg_key_mag->ts02n_key_def_s->key2_long_press_cb();
            TUYA_APP_LOG_DEBUG("key2 is long pressed");
        }
        /* Short press */
        if (sg_key_mag->ts02n_key_def_s->key2_short_press_cb != NULL) {
            if (is_key_release_to_release_less_time(KEY2_CODE, sg_key_mag->ts02n_key_def_s->key2_long_press_time)) {
                sg_key_mag->ts02n_key_def_s->key2_short_press_cb();
                TUYA_APP_LOG_DEBUG("key2 is pressed");
            }
        }
    } else {
        /* Short press */
        if (sg_key_mag->ts02n_key_def_s->key2_short_press_cb != NULL) {
            if (is_key_press_over_time(KEY2_CODE, KEY_PRESS_SHORT_TIME)) {
                sg_key_mag->ts02n_key_def_s->key2_short_press_cb();
                TUYA_APP_LOG_DEBUG("key2 is pressed");
            }
        }
    }
}

/* Key timing cycle function */
void ts02n_key_loop(void)
{
    static uint32_t s_key_scan_tm = 0;
	/* Timing judgment */
	if (!clock_time_exceed(s_key_scan_tm, (sg_key_mag->ts02n_key_def_s->scan_time)*1000)) {
		return;
	}
	s_key_scan_tm = clock_time();	/* Record current time */
    update_key_status(sg_key_mag->ts02n_key_def_s->scan_time);	/* Scan key status */
    detect_and_handle_key_event();	/* Judgment and handling */
}

(5) Temperature acquisition and processing

According to the introduction of NTC temperature sensor in the hardware scheme, the current temperature value can be converted through the port voltage value collected by ADC module. Since the chip platform used in this case does not support floating-point operation, we use the look-up table method to obtain the current temperature. Firstly, we write a script to read out and convert the resistance value in the R-T table of NTC into voltage value, and then put it into the array in order for storage, so that we can convert the voltage value into temperature value by querying the array. Scripts are available in tuya-iotos-embeded-demo-ble-temperature-alarm Get from.

a. Temperature acquisition

NTC port settings are as follows:

Temperature sensorPin
Temperature sensor P_NTCADC/GPIO_PB6

Code implementation:

#define P_NTC               	GPIO_PB6	/* ADC */
#define TEMP_ARRAY_MIN_VALUE    0 			/*  The temperature corresponding to the first data in the array*/
#define TEMP_ARRAY_SIZE         120 			/*  Number of data*/

/* NTC(B3950/100K) Temperature voltage correspondence table */
const uint16_t vol_data_of_temp[TEMP_ARRAY_SIZE] = {
     190,  199,  209,  219,  229,  240,  251,  263,  275,  288,  301,  314,  328,  342,  357,  372,  388,  404,  420,  437, /* 0 ~ 19 */
     455,  473,  491,  510,  530,  549,  570,  591,  612,  634,  656,  679,  702,  725,  749,  774,  799,  824,  849,  875, /* 20 ~ 39 */
     902,  928,  955,  982, 1010, 1038, 1066, 1094, 1123, 1152, 1181, 1210, 1239, 1268, 1298, 1327, 1357, 1386, 1416, 1446, /* 40 ~ 59 */
    1475, 1505, 1535, 1564, 1593, 1623, 1652, 1681, 1710, 1738, 1767, 1795, 1823, 1851, 1878, 1906, 1933, 1959, 1986, 2012, /* 60 ~ 79 */
    2038, 2063, 2088, 2113, 2138, 2162, 2185, 2209, 2232, 2255, 2277, 2299, 2320, 2342, 2362, 2383, 2403, 2423, 2442, 2461, /* 80 ~ 99 */
    2480, 2498, 2516, 2534, 2551, 2568, 2584, 2600, 2616, 2632, 2647, 2662, 2676, 2690, 2704, 2718, 2731, 2744, 2757, 2769  /* 100 ~ 119 */
};

/* NTC Port and ADC module initialization */
void ntc_adc_init(void)
{
	adc_init();
	adc_base_init(P_NTC);
	adc_power_on_sar_adc(1);
}

/* Judge whether the voltage value is between data[num1] and data[num2] */
static uint8_t is_vol_value_between(uint16_t value, uint8_t num1, uint8_t num2)
{
    if ((value >= vol_data_of_temp[num1]) && (value <= vol_data_of_temp[num2])) {
        return 1;
    } else {
        return 0;
    }
}

/* Judge whether the voltage value is closer to data[num1] or data[num2] */
static uint8_t get_closer_num(uint16_t value, uint8_t num1, uint8_t num2)
{
    if ((value - vol_data_of_temp[num1]) < (vol_data_of_temp[num2] - value)) {
        return num1;
    } else {
        return num2;
    }
}

/* Convert voltage value to temperature value */
static uint8_t transform_vol_to_temp(uint16_t vol_value)
{
    uint8_t comp_num;
    uint8_t min = 0;
    uint8_t max = TEMP_ARRAY_SIZE - 1;
    uint8_t temp = 0;

    if (vol_value <= vol_data_of_temp[min]) {
        return TEMP_ARRAY_MIN_VALUE;
    }
    if (vol_value >= vol_data_of_temp[max]) {
        return (TEMP_ARRAY_MIN_VALUE + TEMP_ARRAY_SIZE - 1);
    }
    while (1) {
        comp_num = (max + min) / 2;
        if (vol_value == vol_data_of_temp[comp_num]) {
            temp = comp_num + TEMP_ARRAY_MIN_VALUE;
            break;
        } else if (vol_value < vol_data_of_temp[comp_num]) {
            if (is_vol_value_between(vol_value, comp_num-1, comp_num)) {
                temp = get_closer_num(vol_value, comp_num-1, comp_num) + TEMP_ARRAY_MIN_VALUE;
                break;
            } else {
                max = comp_num;
            }
        } else {
            if (is_vol_value_between(vol_value, comp_num, comp_num+1)) {
                temp = get_closer_num(vol_value, comp_num, comp_num+1) + TEMP_ARRAY_MIN_VALUE;
                break;
            } else {
                min = comp_num;
            }
        }
    }
    return temp;
}

/* Get current temperature */
uint8_t get_cur_temp(void)
{
    uint8_t ntc_temp;
    uint16_t ntc_vol_value;
    /* NTC Initialization of port and ADC module (initialization is required before each reading) */
    ntc_adc_init();
    /* Read the A/D conversion result (the voltage value in mV is directly obtained here) */
    ntc_vol_value = (uint16_t)adc_sample_and_get_result();
    TUYA_APP_LOG_DEBUG("voltage: %d", ntc_vol_value);
    /* Convert voltage value to temperature value */
    ntc_temp = transform_vol_to_temp(ntc_vol_value);
    TUYA_APP_LOG_DEBUG("temperature: %d", ntc_temp);
    return ntc_temp;
}

(6) Application layer function implementation

After the implementation of the above relevant driver code, the following is the implementation of the relevant functions of the intelligent kettle.

a. Preparation of initialization function and main loop function

/* The initialization function is called in the tuya_ble_app_init() function of tuya_ble_app_demo.c. */
void tuya_app_kettle_init(void)
{
    memset(&g_kettle, 0, sizeof(g_kettle));				/* Variable initialization */
    memset(&g_kettle_flag, 0, sizeof(g_kettle_flag));	/* Flag initialization */
    set_keep_warm_temp(TEMP_KEEP_WARM_DEFAULT);			/* Set default holding temperature */

    led_init();											/* Led port initialization */
    relay_init();										/* Relay port initialization */
    buzzer_pwm_init();									/* Buzzer port and PWM module initialization */
    ntc_adc_init();										/* Initialization of temperature sensor port and ADC module */
    ts02n_key_init(&user_ts02n_key_def_s);				/* Press to register and initialize */
    ble_connect_status_init();							/* Bluetooth connection status initialization (described in the next section) */
}

/* The main loop function is called in the app_exe() function of tuya_ble_app_demo.c. */
void tuya_app_kettle_loop(void)
{
	update_ble_status();								/* Bluetooth connection status update (described in the next section) */
    update_cur_temp();									/* Temperature update processing */
    ts02n_key_loop();									/* Key cycle processing */
    update_kettle_mode();								/* Mode update processing */
    update_led_green_status();							/* Indicator timing processing */
    update_buzzer_status();								/* Buzzer timing processing */
}

b. Temperature update and fault detection

Update the temperature value every 2 seconds. If there is a temperature change, report the data update to the cloud and carry out a fault detection; If the fault status changes, update the fault information to the cloud:

#define TEMP_UPPER_LIMIT        105 			/*  Alarm temperature threshold*/
#define TIME_ GET_ Temp 2000 / * temperature update interval: 2s*/

typedef BYTE_T FAULT_E; 					/* fault */
#define FAULT_NORMAL            0x00 		/*  Normal*/
#define FAULT_LACK_WATER        0x01 		/*  Lack of water*/

/* Update fault information */
static void update_fault(FAULT_E fault)
{
    g_kettle.fault = fault;
    report_one_dp_data(DP_ID_FAULT, g_kettle.fault);
    TUYA_APP_LOG_DEBUG("fault: %d", g_kettle.fault);
}

/* The kettle stops working */
static void stop_kettle(void)
{
    set_boil_turn(OFF);
    set_keep_warm_turn(OFF);
    set_led_red(OFF);
    set_led_orange(OFF);
    set_led_green_status(OFF);
    set_relay(OFF);
}

/* Fault detection and handling */
static void detect_and_handle_fault_event(void)
{
    if (g_kettle.fault == FAULT_NORMAL) {
        if (g_kettle.temp_cur >= TEMP_UPPER_LIMIT) {	/* When the upper temperature limit is exceeded */
            update_fault(FAULT_LACK_WATER);				/* Update the fault status to water shortage and dry burning */
            set_buzzer_mode(BUZZER_MODE_FAULT);			/* The buzzer keeps ringing */
            stop_kettle();								/* Stop working */
        }
    } else {
        if (g_kettle.temp_cur < TEMP_UPPER_LIMIT) {		/* After temperature recovery */
            update_fault(FAULT_NORMAL);					/* Update the fault status to no fault */
            set_buzzer_mode(BUZZER_MODE_STOP);			/* Buzzer stop */
            set_work_mode(MODE_NATURE);					/* Switch to natural mode */
        }
    }
}

/* Update current temperature */
static void update_cur_temp(void)
{
    uint8_t temp;
    static uint32_t s_get_temp_tm = 0;
	/* 2 Second timing */
    if (!clock_time_exceed(s_get_temp_tm, TIME_GET_TEMP*1000)) {
        return;
    }
    s_get_temp_tm = clock_time();
	/* Get current temperature */
    temp = get_cur_temp();
    if (g_kettle.temp_cur != temp) {		/* Temperature change? */
        g_kettle.temp_cur = temp;			/* Update current temperature */
        report_one_dp_data(DP_ID_TEMP_CUR, g_kettle.temp_cur);
        detect_and_handle_fault_event();	/* Fault detection and handling */
    }
}

c. Key registration and event response processing

The main response contents of key pins and related operations are set as follows:

KeyPinoperationresponse
Boiling key P_KEY_BOILP7/GPIO_PC3TouchTurn the boiling function on / off
Insulation key P_KEY_KEEPP8/GPIO_PC2Touch
Press and hold for 5 seconds
Turn insulation on / off
Enter distribution network status

Key information is registered as follows:

#define P_KEY_BOIL	GPIO_PC3
#define P_KEY_KEEP	GPIO_PC2

/* User key information registration */
TS02N_KEY_DEF_T user_ts02n_key_def_s = {
    .key1_pin = P_KEY_BOIL,         					/* P7 */
    .key2_pin = P_KEY_KEEP,         					/* P8 */
    .key1_short_press_cb = key_boil_short_press_cb_fun,	/* Tap the boiling key */
    .key2_short_press_cb = key_keep_short_press_cb_fun,	/* Heat preservation key touch processing */
    .key1_long_press_cb = NULL,							/* No boiling key long press function */
    .key2_long_press_cb = key_keep_long_press_cb_fun,	/* Press the heat preservation key for 5 seconds */
    .key1_long_press_time = 0,      					/* No boiling key long press function */
    .key2_long_press_time = 5000,   					/* 5s */
    .scan_time = 10,                					/* 10ms */
};

/* Switch boiling on / off */
static void switch_boil_turn(void)
{
    if (g_kettle.boil_turn == ON) {
        set_boil_turn(OFF);		/* Turn off the boiling function (see [cloud control (3)] for details) */
    } else {
        set_boil_turn(ON);		/* Turn on the boiling function */
    }
}

/* Switch insulation on / off */
static void switch_keep_warm_turn(void)
{
    if (g_kettle.keep_warm_turn == ON) {
        set_keep_warm_turn(OFF);/* Turn off the insulation function (the specific implementation content is described in [cloud control (3)]) */
    } else {
        set_keep_warm_turn(ON);	/* Turn on the insulation function */
    }
}

/* Tap the boiling key */
void key_boil_short_press_cb_fun(void)
{
    if (g_kettle.fault != FAULT_NORMAL) {	/* The key is invalid when the fault occurs */
        return;
    }
    switch_boil_turn();						/* Switch boiling on / off */
    set_buzzer_mode(BUZZER_MODE_ONCE);		/* Set the buzzer mode to "one drop" */
}

/* Heat preservation key touch processing */
void key_keep_short_press_cb_fun(void)
{
    if (g_kettle.fault != FAULT_NORMAL) {	/* The key is invalid when the fault occurs */
        return;
    }
    switch_keep_warm_turn();				/* Switch insulation on / off */
    set_buzzer_mode(BUZZER_MODE_ONCE);		/* Set the buzzer mode to "one drop" */
}

/* Press the heat preservation key for 5 seconds */
void key_keep_long_press_cb_fun(void)
{
    if (F_BLE_BONDING == SET) {				/* Invalid when bound by user */
        return;
    }
    try_to_connect_ble();					/* Try distribution network (the specific implementation content is described in [cloud control (1)]) */
    set_buzzer_mode(BUZZER_MODE_ONCE);		/* Set the buzzer mode to "one drop" */
}

d. Mode update and processing

According to the function settings, we split the working mode of the kettle into the following four modes:

patternconditionstate
Natural modelBoiling and heat preservation functions are turned offThe indicator light and heating are off
Boiling modeBoiling function onRed light on, orange light off, green light off, heating on
Insulation mode 1Boiling function is off, heat preservation function is on, tap water modeRed light off, orange light on, green light off, heating on
Insulation mode 2Boiling function is off, heat preservation function is on, purified water modeRed light off, orange light / green light / heating is set according to the current temperature

Code implementation:

/* Working mode */
typedef BYTE_T MODE_E; 
#define MODE_NATURE             0x00
#define MODE_BOIL               0x01
#define MODE_KEEP_WARM1         0x02
#define MODE_KEEP_WARM2         0x03
/* Water type */
typedef BYTE_T WATER_TYPE_E; 
#define WATER_TYPE_TAP          0x00
#define WATER_TYPE_PURE         0x01
/* Temperature dependent */
#define TEMP_BOILED             97 	/*  Boiling temperature*/
#define TEMP_KEEP_WARM_DEFAULT  55 	/*  Default holding temperature*/

/* Natural model */
static void kettle_mode_nature(void)
{
    set_led_red(OFF);
    set_led_orange(OFF);
    set_led_green_status(OFF);
    set_relay(OFF);
}

/* Boiling mode */
static void kettle_mode_boil(void)
{
    if (g_kettle.temp_cur >= TEMP_BOILED) {	/* When the boiling temperature is reached */
        set_water_type(WATER_TYPE_PURE);	/* The type of renewal water is purified water */
        set_boil_turn(OFF);					/* Turn off the boiling function */
        set_relay(OFF);						/* Turn off heating */
    } else {
        set_relay(ON);						/* Turn on heating */
    }
}

/* Insulation mode 1 */
static void kettle_mode_keep_warm1(void)
{
    if (g_kettle.temp_cur >= TEMP_BOILED) {	/* When the boiling temperature is reached */
        set_water_type(WATER_TYPE_PURE);	/* The type of renewal water is purified water */
        set_relay(OFF);						/* Turn off heating */
    } else {
        set_relay(ON);						/* Turn on heating */
    }
}

/* Insulation mode 2 */
static void kettle_mode_keep_warm2(void)
{
	/* When the insulation temperature is not reached, the orange light is on, the green light is off, the heating is turned off when the temperature is high, and the heating is turned on when the temperature is low */
    if (g_kettle.temp_cur > g_kettle.temp_set) {
        set_led_orange(ON);
        set_led_green_status(OFF);
        set_relay(OFF);
    } else if (g_kettle.temp_cur < (g_kettle.temp_set - 3)) {
        set_led_orange(ON);
        set_led_green_status(OFF);
        set_relay(ON);
    /* When the insulation temperature is reached, the orange light is off, the green light is on, and the heating is off */
    } else {
        set_led_orange(OFF);
        set_led_green_status(ON);
        set_relay(OFF);
    }
}

/* Operation modes */
static void run_kettle(void)
{
    if (g_kettle.fault != FAULT_NORMAL) {
        return;
    }
    switch (g_kettle.mode) {
    case MODE_NATURE:
        kettle_mode_nature();
        break;
    case MODE_BOIL:
        kettle_mode_boil();
        break;
    case MODE_KEEP_WARM1:
        kettle_mode_keep_warm1();
        break;
    case MODE_KEEP_WARM2:
        kettle_mode_keep_warm2();
        break;
    default:
        break;
    }
}

/* Mode update */
static void update_kettle_mode(void)
{
    if (g_kettle.boil_turn == ON) {					/* When the boiling function is on */
        set_work_mode(MODE_BOIL);					/* Enter boiling mode */
    } else if (g_kettle.keep_warm_turn == ON){		/* When boiling function is off and heat preservation function is on */
        if (g_kettle.water_type == WATER_TYPE_TAP) {/* When using tap water */
            set_work_mode(MODE_KEEP_WARM1);			/* Enter insulation mode 1 (boil first and then keep warm) */
        } else {									/* When using purified water */
            set_work_mode(MODE_KEEP_WARM2);			/* Enter insulation mode 2 (direct insulation) */
        }
    } else {										/* When boiling and heat preservation functions are turned off and on */
        set_work_mode(MODE_NATURE);					/* Enter natural mode */
    }
    run_kettle();
}

The above is how to realize the offline control function of Bluetooth intelligent kettle. If you have any questions or have a better scheme, please leave a message for discussion~

Keywords: Embedded system IoT

Added by mountaindave on Fri, 14 Jan 2022 00:39:04 +0200