Sandwich IoT
Published © GPL3+

Prototype a Smart Neck Massager

We retrofit a traditional neck massager and make it smart to melt stress away

BeginnerFull instructions provided24 hours182
Prototype a Smart Neck Massager

Things used in this project

Hardware components

BTU network module
A Bluetooth Low Energy module for connecting a device to the cloud.
×1
NTC thermistor (MF52B)
NTC thermistor is epoxy resin-coated in small size, used for temperature measurement. It features a wide range of resistance, high precision stability and sensitivity, as well as fast response.
×1
Heating wire
Used to conduct heat to support thermostat heating feature.
×1
WTN6 voice chip
In one-line serial port mode, the BTU module can control and send data to the voice chip through the DATA line to have voice play, stop, loop, and more.
×1
Electrode pad
Two stainless steel electrode pads on the massager.
×1
Rechargeable battery
We bought a 700 mAh lithium polymer battery with a 3.7V rated voltage and 4.2V charging voltage. It has built-in protection circuit to prevents overcharge, over-discharge, over-current, and short circuit.
×1

Software apps and online services

Tuya Smart app or Smart Life app
https://developer.tuya.com/en/docs/iot/tuya-smart-app-smart-life-app-advantages?id=K989rqa49rluq
Sample code in GitHub
https://github.com/TuyaInc/tuya_ble_sdk_Demo_Project_tlsr8253

Story

Read more

Code

Code snippet #13

Plain text
int switching_heat(unsigned char warm)
{
	if (warm > 1) {
		TUYA_APP_LOG_ERROR("*********No such model!!!**********");
	}
//	printf("wram%d massage_state.heat%d\r\n", warm, massage_state.heat);

	switch (warm) {
	case strong_heat:
		TUYA_APP_LOG_INFO("**********strong_heat************");
		gpio_write(HEAT_PIN, 1);
		temperature_detection();
		break;
	case off_heat:
		TUYA_APP_LOG_INFO("**********off_heat************");
		gpio_write(HEAT_PIN, 0);
		break;

	default:
		break;
	}

	return 0;
}

Code snippet #14

Plain text
int switching_heat(unsigned char warm)
{
	if (warm > 1) {
		TUYA_APP_LOG_ERROR("*********No such model!!!**********");
	}
//	printf("wram%d massage_state.heat%d\r\n", warm, massage_state.heat);

	switch (warm) {
	case strong_heat:
		TUYA_APP_LOG_INFO("**********strong_heat************");
		gpio_write(HEAT_PIN, 1);
		temperature_detection();
		break;
	case off_heat:
		TUYA_APP_LOG_INFO("**********off_heat************");
		gpio_write(HEAT_PIN, 0);
		break;

	default:
		break;
	}

	return 0;
}

Code snippet #15

Plain text
/*Temperature detection, which is called when heating feature is in high or low level.*/
int temperature_detection(void)
{
	int Rntc = 0, Vcc = 0;
	adc_channel_checkout(channel_x1);
	Vcc = adc_sample_and_get_result();	 // Unit: mV
	Rntc = Vcc*R25 / (3300-Vcc);
	TUYA_APP_LOG_INFO("Rntc_val=%dΩ", Rntc);
	if (Rntc >= 5311) {  	// NTC resistance value is 5311Ω at 40°C.
		TUYA_APP_LOG_WARNING("********High Temperature Warning!!!********");
		 gpio_write(HEAT_PIN, 0);  // When the temperature exceeds 40°C, heating will be turned off.
	}

	return 0;
}

Code snippet #16

Plain text
/*Temperature detection, which is called when heating feature is in high or low level.*/
int temperature_detection(void)
{
	int Rntc = 0, Vcc = 0;
	adc_channel_checkout(channel_x1);
	Vcc = adc_sample_and_get_result();	 // Unit: mV
	Rntc = Vcc*R25 / (3300-Vcc);
	TUYA_APP_LOG_INFO("Rntc_val=%dΩ", Rntc);
	if (Rntc >= 5311) {  	// NTC resistance value is 5311Ω at 40°C.
		TUYA_APP_LOG_WARNING("********High Temperature Warning!!!********");
		 gpio_write(HEAT_PIN, 0);  // When the temperature exceeds 40°C, heating will be turned off.
	}

	return 0;
}

Code snippet #17

Plain text
void boost_init(void)
{
	gpio_set_func(BOOST_PIN, AS_PWM0);
	gpio_set_output_en(BOOST_PIN, 1);
	// PWM0 1ms cycle
	pwm_set_mode(PWM0_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, BOOST_PWM_CLOCK_HZ); // When voltage is stepped up, the frequency of PWM is 16M / (968-1) ≈ 16.55 kHz.
	pwm_set_phase(PWM0_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
	pwm_start(PWM0_ID);
}

Code snippet #18

Plain text
void boost_init(void)
{
	gpio_set_func(BOOST_PIN, AS_PWM0);
	gpio_set_output_en(BOOST_PIN, 1);
	// PWM0 1ms cycle
	pwm_set_mode(PWM0_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, BOOST_PWM_CLOCK_HZ); // When voltage is stepped up, the frequency of PWM is 16M / (968-1) ≈ 16.55 kHz.
	pwm_set_phase(PWM0_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
	pwm_start(PWM0_ID);
}

Code snippet #19

Plain text
void switching_gear(unsigned char gears)
{
	if (gears > 15) {
		TUYA_APP_LOG_ERROR("*********There is no such gear!!!**********");
	}

	switch (gears) {
	case first_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (10 * CLOCK_SYS_CLOCK_1US) );

		break;
	case second_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (20 * CLOCK_SYS_CLOCK_1US) );

		break;
	case third_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (40 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fourth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (60 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fifth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (70 * CLOCK_SYS_CLOCK_1US) );

		break;
	case sixth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (90 * CLOCK_SYS_CLOCK_1US) );

		break;
	case seventh_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (120 * CLOCK_SYS_CLOCK_1US) );

		break;
	case eighth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (160 * CLOCK_SYS_CLOCK_1US) );

		break;
	case ninth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (180 * CLOCK_SYS_CLOCK_1US) );

		break;
	case tenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (220 * CLOCK_SYS_CLOCK_1US) );

		break;
	case eleventh_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (240 * CLOCK_SYS_CLOCK_1US) );

		break;
	case twelfth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (260 * CLOCK_SYS_CLOCK_1US) );

		break;
	case thirteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (300 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fourteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (340 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fifteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (360 * CLOCK_SYS_CLOCK_1US) );

		break;
	case max_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (360 * CLOCK_SYS_CLOCK_1US) );

		break;
	default:
		break;
	}
}

Code snippet #20

Plain text
void switching_gear(unsigned char gears)
{
	if (gears > 15) {
		TUYA_APP_LOG_ERROR("*********There is no such gear!!!**********");
	}

	switch (gears) {
	case first_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (10 * CLOCK_SYS_CLOCK_1US) );

		break;
	case second_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (20 * CLOCK_SYS_CLOCK_1US) );

		break;
	case third_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (40 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fourth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (60 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fifth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (70 * CLOCK_SYS_CLOCK_1US) );

		break;
	case sixth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (90 * CLOCK_SYS_CLOCK_1US) );

		break;
	case seventh_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (120 * CLOCK_SYS_CLOCK_1US) );

		break;
	case eighth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (160 * CLOCK_SYS_CLOCK_1US) );

		break;
	case ninth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (180 * CLOCK_SYS_CLOCK_1US) );

		break;
	case tenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (220 * CLOCK_SYS_CLOCK_1US) );

		break;
	case eleventh_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (240 * CLOCK_SYS_CLOCK_1US) );

		break;
	case twelfth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (260 * CLOCK_SYS_CLOCK_1US) );

		break;
	case thirteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (300 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fourteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (340 * CLOCK_SYS_CLOCK_1US) );

		break;
	case fifteenth_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (360 * CLOCK_SYS_CLOCK_1US) );

		break;
	case max_gear:
		pwm_set_cycle_and_duty(PWM0_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (360 * CLOCK_SYS_CLOCK_1US) );

		break;
	default:
		break;
	}
}

Code snippet #21

Plain text
/***********************************************************
*   Function:  write_massage_status_to_flash
*   Input:     none
*   Output:    none
*   Return:    none
*   Notice:    Write massager status to the flash memory.
***********************************************************/
void write_massage_status_to_flash(void)
{
	Flash_Write_Buff[0] = massage_state.on_off;
	Flash_Write_Buff[1] = massage_state.pattern;
	Flash_Write_Buff[2] = massage_state.gear;
	Flash_Write_Buff[3] = massage_state.heat;

	flash_write_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Write_Buff);

	return;
}

Code snippet #22

Plain text
/***********************************************************
*   Function:  write_massage_status_to_flash
*   Input:     none
*   Output:    none
*   Return:    none
*   Notice:    Write massager status to the flash memory.
***********************************************************/
void write_massage_status_to_flash(void)
{
	Flash_Write_Buff[0] = massage_state.on_off;
	Flash_Write_Buff[1] = massage_state.pattern;
	Flash_Write_Buff[2] = massage_state.gear;
	Flash_Write_Buff[3] = massage_state.heat;

	flash_write_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Write_Buff);

	return;
}

Code snippet #5

Plain text
void pattern_pin_init(void)
{
	gpio_set_func(PATTERN_PIN_A, AS_PWM1_N);
	gpio_set_func(PATTERN_PIN_B, AS_PWM5);
	gpio_set_func(HEAT_PIN, AS_GPIO);

	gpio_set_output_en(PATTERN_PIN_A, 1);
	gpio_set_output_en(PATTERN_PIN_B, 1);
	gpio_set_output_en(HEAT_PIN, 1);

	gpio_write(PATTERN_PIN_A, 0);
	gpio_write(PATTERN_PIN_B, 0);
//    gpio_write(HEAT_PIN, 1);

		//PWM0 1ms cycle  26.5% duty 	 1,000 Hz
	pwm_set_mode(PWM1_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
	pwm_set_phase(PWM1_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
	pwm_polo_enable(PWM1_ID, 1);  // Enable the PWM polarity
	pwm_start(PWM1_ID);

	 // PWM5 1 ms cycle, 26.5% duty  1,000 Hz
	pwm_set_mode(PWM5_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
	pwm_set_phase(PWM5_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM5_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );

}

Code snippet #23

Plain text
/**********************************************************************
*   Function:  read_massage_status_to_flash
*   Input:     none
*   Output:    none
*   Return:    none
* Notice:   Read the massager status data before power-off from the flash memory, and save it to the status struct.
**********************************************************************/
void read_massage_status_to_flash(void)
{
	flash_read_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Read_Buff);

	// Store the data read from the flash memory into the struct.
	massage_state.on_off = Flash_Read_Buff[0];
	massage_state.pattern = Flash_Read_Buff[1];
	massage_state.gear = Flash_Read_Buff[2];
	massage_state.heat = Flash_Read_Buff[3];

	return;
}

Code snippet #24

Plain text
/**********************************************************************
*   Function:  read_massage_status_to_flash
*   Input:     none
*   Output:    none
*   Return:    none
* Notice:   Read the massager status data before power-off from the flash memory, and save it to the status struct.
**********************************************************************/
void read_massage_status_to_flash(void)
{
	flash_read_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Read_Buff);

	// Store the data read from the flash memory into the struct.
	massage_state.on_off = Flash_Read_Buff[0];
	massage_state.pattern = Flash_Read_Buff[1];
	massage_state.gear = Flash_Read_Buff[2];
	massage_state.heat = Flash_Read_Buff[3];

	return;
}

Code snippet #25

Plain text
/***********************************************************
*   Function:  erase_massage_flash
*   Input:     none
*   Output:    none
*   Return:    none
*   Notice:    Restore defaults
***********************************************************/
void erase_massage_flash(void)
{
	massage_state.on_off = OFF;
	massage_state.pattern = relieve;
	massage_state.gear = first_gear;
	massage_state.heat = off_heat;

	Flash_Write_Buff[0] = OFF;
	Flash_Write_Buff[1] = relieve;
	Flash_Write_Buff[2] = first_gear;
	Flash_Write_Buff[3] = off_heat;

	flash_write_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Write_Buff);

	return;
}

Code snippet #26

Plain text
/***********************************************************
*   Function:  erase_massage_flash
*   Input:     none
*   Output:    none
*   Return:    none
*   Notice:    Restore defaults
***********************************************************/
void erase_massage_flash(void)
{
	massage_state.on_off = OFF;
	massage_state.pattern = relieve;
	massage_state.gear = first_gear;
	massage_state.heat = off_heat;

	Flash_Write_Buff[0] = OFF;
	Flash_Write_Buff[1] = relieve;
	Flash_Write_Buff[2] = first_gear;
	Flash_Write_Buff[3] = off_heat;

	flash_write_page(FLASH_ADDR, FLASH_BUFF_LEN, (unsigned char *)Flash_Write_Buff);

	return;
}

Code snippet #29

Plain text
void app_dp_handle(uint8_t *dp_data)
{
	printf("dp_data:%d  %d  %d  %d\r\n", dp_data[0], dp_data[1], dp_data[2], dp_data[3]);
	switch (dp_data[0]) {
	case 0x66:
		if (dp_data[3] == strong_heat) {
			massage_state.heat = strong_heat;
		} else {
			massage_state.heat = off_heat;
		}
		printf("dp_data[3]:%d massage_state.heat:%d\r\n", dp_data[3], massage_state.heat);
		if (!app_flag) {
			switching_heat(massage_state.heat);
		}
		break;
	case 0x67:
		printf("dp_data[3]:%d \r\n", dp_data[3]);

		switch (dp_data[3]) {
		case first_gear:
			massage_state.gear = first_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case second_gear:
			massage_state.gear = second_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case third_gear:
			massage_state.gear = third_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fourth_gear:
			massage_state.gear = fourth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fifth_gear:
			massage_state.gear = fifth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case sixth_gear:
			massage_state.gear = sixth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case seventh_gear:
			massage_state.gear = seventh_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case eighth_gear:
			massage_state.gear = eighth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case ninth_gear:
			massage_state.gear = ninth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case tenth_gear:
			massage_state.gear = tenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case eleventh_gear:
			massage_state.gear = eleventh_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case twelfth_gear:
			massage_state.gear = twelfth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case thirteenth_gear:
			massage_state.gear = thirteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fourteenth_gear:
			massage_state.gear = fourteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fifteenth_gear:
			massage_state.gear = fifteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case max_gear:
			massage_state.gear = max_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		default:
			break;
		}
		break;
	case 0x68:
		if (dp_data[3] == relieve) {
			massage_state.pattern = relieve;

		} else if (dp_data[3] == vitality) {
			massage_state.pattern = vitality;
		} else if (dp_data[3] == hammering) {
			massage_state.pattern = hammering;
		} else if (dp_data[3] == scraping_therapy) {
			massage_state.pattern = scraping_therapy;
		} else {
			massage_state.pattern = intelligent;
		}

		break;
	case 0x69:
		if (dp_data[3] == ON) {
			massage_state.on_off = ON;
			rs2255_init();
			voice_prompt_init();
			pattern_pin_init();
			app_flag = 0;
		} else {
			massage_state.on_off = OFF;
			power_off_init();
			app_flag = 1;
		}
		break;

	default:
		break;
	}
}

Code snippet #30

Plain text
void app_dp_handle(uint8_t *dp_data)
{
	printf("dp_data:%d  %d  %d  %d\r\n", dp_data[0], dp_data[1], dp_data[2], dp_data[3]);
	switch (dp_data[0]) {
	case 0x66:
		if (dp_data[3] == strong_heat) {
			massage_state.heat = strong_heat;
		} else {
			massage_state.heat = off_heat;
		}
		printf("dp_data[3]:%d massage_state.heat:%d\r\n", dp_data[3], massage_state.heat);
		if (!app_flag) {
			switching_heat(massage_state.heat);
		}
		break;
	case 0x67:
		printf("dp_data[3]:%d \r\n", dp_data[3]);

		switch (dp_data[3]) {
		case first_gear:
			massage_state.gear = first_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case second_gear:
			massage_state.gear = second_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case third_gear:
			massage_state.gear = third_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fourth_gear:
			massage_state.gear = fourth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fifth_gear:
			massage_state.gear = fifth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case sixth_gear:
			massage_state.gear = sixth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case seventh_gear:
			massage_state.gear = seventh_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case eighth_gear:
			massage_state.gear = eighth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case ninth_gear:
			massage_state.gear = ninth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case tenth_gear:
			massage_state.gear = tenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case eleventh_gear:
			massage_state.gear = eleventh_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case twelfth_gear:
			massage_state.gear = twelfth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case thirteenth_gear:
			massage_state.gear = thirteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fourteenth_gear:
			massage_state.gear = fourteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case fifteenth_gear:
			massage_state.gear = fifteenth_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		case max_gear:
			massage_state.gear = max_gear;
			if (!app_flag) {
				switching_gear(massage_state.gear);
			}
			break;
		default:
			break;
		}
		break;
	case 0x68:
		if (dp_data[3] == relieve) {
			massage_state.pattern = relieve;

		} else if (dp_data[3] == vitality) {
			massage_state.pattern = vitality;
		} else if (dp_data[3] == hammering) {
			massage_state.pattern = hammering;
		} else if (dp_data[3] == scraping_therapy) {
			massage_state.pattern = scraping_therapy;
		} else {
			massage_state.pattern = intelligent;
		}

		break;
	case 0x69:
		if (dp_data[3] == ON) {
			massage_state.on_off = ON;
			rs2255_init();
			voice_prompt_init();
			pattern_pin_init();
			app_flag = 0;
		} else {
			massage_state.on_off = OFF;
			power_off_init();
			app_flag = 1;
		}
		break;

	default:
		break;
	}
}

Code snippet #6

Plain text
void pattern_pin_init(void)
{
	gpio_set_func(PATTERN_PIN_A, AS_PWM1_N);
	gpio_set_func(PATTERN_PIN_B, AS_PWM5);
	gpio_set_func(HEAT_PIN, AS_GPIO);

	gpio_set_output_en(PATTERN_PIN_A, 1);
	gpio_set_output_en(PATTERN_PIN_B, 1);
	gpio_set_output_en(HEAT_PIN, 1);

	gpio_write(PATTERN_PIN_A, 0);
	gpio_write(PATTERN_PIN_B, 0);
//    gpio_write(HEAT_PIN, 1);

		//PWM0 1ms cycle  26.5% duty 	 1,000 Hz
	pwm_set_mode(PWM1_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
	pwm_set_phase(PWM1_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
	pwm_polo_enable(PWM1_ID, 1);  // Enable the PWM polarity
	pwm_start(PWM1_ID);

	 // PWM5 1 ms cycle, 26.5% duty  1,000 Hz
	pwm_set_mode(PWM5_ID, PWM_NORMAL_MODE);
	pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
	pwm_set_phase(PWM5_ID, 0);   // No phase at PWM beginning
	pwm_set_cycle_and_duty(PWM5_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );

}

Code snippet #7

Plain text
void switching_pattern(unsigned char pat)
{
	if (pat > 4) {
		TUYA_APP_LOG_ERROR("*********No such model!!!**********");
	}

	switch (pat) {
	case relieve:
		pwm_start(PWM5_ID);
			sleep_us(450);	 	// Delay 480 μs to prevent the triode from being burned out.
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(30 * TIME_MS);
		break;

	case vitality:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(20 * TIME_MS);
		break;

	case hammering:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(40 * TIME_MS);
		break;

	case scraping_therapy:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(50 * TIME_MS);
		break;

	case intelligent:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
			sleep_us(((rand() % 4 + 2) * 10) * TIME_MS);	 // 20 ms, 30 ms, 40 ms, or 50 ms is generated randomly.
		break;
	default:
		break;
	}
	return;
}

Code snippet #8

Plain text
void switching_pattern(unsigned char pat)
{
	if (pat > 4) {
		TUYA_APP_LOG_ERROR("*********No such model!!!**********");
	}

	switch (pat) {
	case relieve:
		pwm_start(PWM5_ID);
			sleep_us(450);	 	// Delay 480 μs to prevent the triode from being burned out.
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(30 * TIME_MS);
		break;

	case vitality:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(20 * TIME_MS);
		break;

	case hammering:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(40 * TIME_MS);
		break;

	case scraping_therapy:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(50 * TIME_MS);
		break;

	case intelligent:
		pwm_start(PWM5_ID);
		sleep_us(450);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (265 * CLOCK_SYS_CLOCK_1US) );
		sleep_us(5 * TIME_MS);
		pwm_stop(PWM5_ID);
		pwm_set_cycle_and_duty(PWM1_ID, (u16) (1000 * CLOCK_SYS_CLOCK_1US),  (u16) (0 * CLOCK_SYS_CLOCK_1US) );
			sleep_us(((rand() % 4 + 2) * 10) * TIME_MS);	 // 20 ms, 30 ms, 40 ms, or 50 ms is generated randomly.
		break;
	default:
		break;
	}
	return;
}

Code snippet #9

Plain text
void voice_prompt_init(void)
{
	gpio_set_func(WTN6_DATA_PIN | WTN6_BUSY_PIN, AS_GPIO);
	gpio_set_input_en(WTN6_BUSY_PIN, 1);
	gpio_set_output_en(WTN6_DATA_PIN, 1);

	gpio_write(WTN6_BUSY_PIN, 0);
}

Code snippet #10

Plain text
void voice_prompt_init(void)
{
	gpio_set_func(WTN6_DATA_PIN | WTN6_BUSY_PIN, AS_GPIO);
	gpio_set_input_en(WTN6_BUSY_PIN, 1);
	gpio_set_output_en(WTN6_DATA_PIN, 1);

	gpio_write(WTN6_BUSY_PIN, 0);
}

Code snippet #11

Plain text
void voice_playing(uint8_t sb_data)
{
	uint8_t s_data, j;
	bool b_data;
	s_data = sb_data;
	gpio_write(WTN6_DATA_PIN, 0);
		sleep_us(5000);	 	// Delay 5 ms
	b_data = s_data & 0X01;

	for (j=0; j<8; j++) {
		if (b_data == 1) {
			gpio_write(WTN6_DATA_PIN, 1);
			   sleep_us(600); // Delay 600 μs
			gpio_write(WTN6_DATA_PIN, 0);
			   sleep_us(200); // Delay 200 μs
		} else {
			gpio_write(WTN6_DATA_PIN, 1);
			   sleep_us(200); // Delay 200 μs
			gpio_write(WTN6_DATA_PIN, 0);
			   sleep_us(600); // Delay 600 μs
		}
			s_data = s_data >> 1;
			b_data = s_data & 0X01;
	}
	gpio_write(WTN6_DATA_PIN, 1);
}

Code snippet #12

Plain text
void voice_playing(uint8_t sb_data)
{
	uint8_t s_data, j;
	bool b_data;
	s_data = sb_data;
	gpio_write(WTN6_DATA_PIN, 0);
		sleep_us(5000);	 	// Delay 5 ms
	b_data = s_data & 0X01;

	for (j=0; j<8; j++) {
		if (b_data == 1) {
			gpio_write(WTN6_DATA_PIN, 1);
			   sleep_us(600); // Delay 600 μs
			gpio_write(WTN6_DATA_PIN, 0);
			   sleep_us(200); // Delay 200 μs
		} else {
			gpio_write(WTN6_DATA_PIN, 1);
			   sleep_us(200); // Delay 200 μs
			gpio_write(WTN6_DATA_PIN, 0);
			   sleep_us(600); // Delay 600 μs
		}
			s_data = s_data >> 1;
			b_data = s_data & 0X01;
	}
	gpio_write(WTN6_DATA_PIN, 1);
}

Github

https://github.com/TuyaInc/tuya_ble_sdk_Demo_Project_tlsr8253

Credits

Sandwich IoT

Sandwich IoT

40 projects • 4 followers

Comments