STM32 with AC solenoid valve with relay and hx711 module for load cell

Hi everyone,
I am using an STM32 MCU. With this MCU, I have connected a relay to control an AC solenoid valve. I have also connected an HX711 module to measure weight using a load cell. I'm using UART to monitor the load cell data on the serial terminal.

In my code, I have created two FreeRTOS threads: one for measuring the load cell weight and another for controlling the relay (i.e., the AC valve). Both threads have the same priority because I want both modules to run in parallel.

However, when I switch the valve using the relay, the HX711 module stops sending data over UART — nothing is printed on the serial terminal. I'm not sure why this is happening. Is it a hardware issue or a software issue?

Please, can anyone help me?

I moved your topic to an appropriate forum category @nadeemmm.

In the future, when creating a topic please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

there are dozens of STM32 development boards - which are you using?

how have you connected and powered the relay, sensors, etc?

when you switch the relay it may drop the power supply voltage which could upset the HX711
alternatively electromagnetic interference when switching the relay could cause problems - I have had problems of microcontrollers resetting when switching relays - overcame problem by using shielded cables

could you upload the code? a small program which demonstrates the problem would do

Hi everyone,

I am using an STM32F407VET6 development board. I have a 5V DC SMPS, which I’m using to power both the relay and the HX711 module. The SMPS ground and the MCU ground are connected (common ground).

To reduce EMI, I’ve connected a snubber circuit across the relay's NO (Normally Open) and COM (Common) terminals.

I’ve created two FreeRTOS threads — one for valve control via the relay, and the other for reading weight data from the HX711 and sending it over UART. Both threads work perfectly until I connect AC power to the solenoid valve.

When the relay receives a HIGH signal from the MCU and switches the AC valve, the second thread (the one reading from HX711 and sending data over UART) stops working. No data is printed on the serial terminal after that.

I’ve attached the serial terminal output and the code for reference.
void Reset_GPIO_Pins(void) {
HAL_GPIO_WritePin(GPIOD,
GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4
| GPIO_PIN_5| GPIO_PIN_8 | GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,
GPIO_PIN_RESET);
}
void Set_Valves(uint32_t *pins, uint8_t pin_count) {
for (uint8_t i = 0; i < pin_count; i++) {
HAL_GPIO_WritePin(GPIOD, pins[i], GPIO_PIN_SET);
}
}
void cycle(uint32_t *pins, uint8_t pin_count,uint32_t delay) {
Set_Valves(pins, pin_count);
osDelay(delay);
Reset_GPIO_Pins();
}
int ValveOperatingTask(void) {
uint32_t cycle_4[] = { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_5,GPIO_PIN_10, GPIO_PIN_11 };
uint32_t cycle_5[] = { GPIO_PIN_13, GPIO_PIN_2, GPIO_PIN_9 };
uint32_t cycle_6[] = { GPIO_PIN_15, GPIO_PIN_14, GPIO_PIN_2, GPIO_PIN_5,GPIO_PIN_10 };
uint32_t cycle_7[] = { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_5,GPIO_PIN_10, GPIO_PIN_11 };

uint32_t cycle_8[] = { GPIO_PIN_13, GPIO_PIN_2, GPIO_PIN_9 };
uint32_t cycle_9[] = { GPIO_PIN_13, GPIO_PIN_4, GPIO_PIN_8 };
uint32_t cycle_10[] = { GPIO_PIN_13, GPIO_PIN_3, GPIO_PIN_12 };

while (1) {
vTaskDelay(pdMS_TO_TICKS(30000));
cycle(cycle_4, sizeof(cycle_4) / sizeof(cycle_4[0]), 10000);
vTaskDelay(pdMS_TO_TICKS(60000));
cycle(cycle_5, sizeof(cycle_5) / sizeof(cycle_5[0]), 60000);
vTaskDelay(pdMS_TO_TICKS(20000));
cycle(cycle_6, sizeof(cycle_6) / sizeof(cycle_6[0]), 10000);
vTaskDelay(pdMS_TO_TICKS(20000));
cycle(cycle_7, sizeof(cycle_7) / sizeof(cycle_7[0]), 22000);
vTaskDelay(pdMS_TO_TICKS(20000));
cycle(cycle_8, sizeof(cycle_8) / sizeof(cycle_8[0]), 10000);
vTaskDelay(pdMS_TO_TICKS(10000));
cycle(cycle_9, sizeof(cycle_9) / sizeof(cycle_9[0]), 10000);
vTaskDelay(pdMS_TO_TICKS(10000));
cycle(cycle_10, sizeof(cycle_10) / sizeof(cycle_10[0]),38000);
vTaskDelay(pdMS_TO_TICKS(20000));
}
}
this is my relay controling pattern which switching the ac valve
void HX711_Init(void) {
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
}

int32_t HX711_Read(void) {
int32_t data = 0;
uint8_t i;

while (HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) == GPIO_PIN_SET)
	;

for (i = 0; i < 24; i++) {
	HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); // SCK
	//HAL_Delay(10);
	data = (data << 1) | HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN);
	HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); // SCK
	//HAL_Delay(100);
}

HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); // 25th pulse
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);

if (data & 0x800000) {
	data |= 0xFF000000;
}

return data;

}
float HX711_GetWeight(int32_t offset, float scale) {
int32_t raw_value = HX711_Read();
float weight = ((float) (raw_value - offset) / scale);

// Avoid displaying small negative values due to noise
if (fabs(raw_value - offset) < 500) {
	return 0.0;
}
return weight;

}
void WeightMeasurementTask(void) {
int32_t offset = 0;
float scale = NAN;
float last_weight = 0.0;
float weight = 0;
float get_wieght = 0;
float density = 1.0;
float volume_ml;
bool valve_active = 0; // State flag for valve operation
bool cycle6_active = 0; // Track if cycle 6 is active
bool cycle2_done = 0; // Flag to track if Cycle 2 (drain water) is complete
bool cycle7_done = 0; // Flag to track if Cycle 7 (fill water) is complete
bool cycle8_done = 0; // Flag to track if Cycle 8 (flush chemical A to V) is complete
bool cycle9_done = 0; // Flag to track if Cycle 9 (flush chemical V to A) is complete
bool cycle10_done = 0; // Flag to track if Cycle 10 (flush chemical DO to DI) is complete

FLASH_FlushCaches();
// Read saved offset and scale from Flash
Flash_ReadData(FLASH_USER_START_ADDR, &offset, sizeof(offset));
Flash_ReadData(FLASH_USER_START_ADDR + sizeof(offset), &scale,
		sizeof(scale));

if (offset == -1 || isnan(scale)) {

osDelay(50);
offset = HX711_Read();
Flash_WriteData(FLASH_USER_START_ADDR, &offset, sizeof(offset));
Flash_WriteData(FLASH_USER_START_ADDR + sizeof(offset), &scale,
sizeof(scale));

}

else {
//printf("Offset and scale loaded from Flash.\n");
// snprintf(buffer5, sizeof(buffer5),
// "Offset and scale loaded from Flash.\n\r:");
// HAL_UART_Transmit(&huart1, (uint8_t*) buffer5, strlen(buffer5),
// HAL_MAX_DELAY);
// //// printf("Offset: %ld, Scale: %.2f\n", offset, scale);
//
// snprintf(buffer6, sizeof(buffer6), "Offset: %ld\n\r:", offset);
// HAL_UART_Transmit(&huart1, (uint8_t*) buffer6, strlen(buffer6),
// HAL_MAX_DELAY);
// snprintf(buffer7, sizeof(buffer7), "Scale: %.2f\n\r:", scale);
// HAL_UART_Transmit(&huart1, (uint8_t*) buffer7, strlen(buffer7),
// HAL_MAX_DELAY);

}

while (1) {
	/* USER CODE END WHILE */

	/* USER CODE BEGIN 3 */

	weight = HX711_GetWeight(offset, scale);
	get_wieght = weight;

// if (weight > 3000.0) {
// snprintf(buffer, sizeof(buffer),
// "Overload! Max weight is 2kg.\n\r");
// HAL_UART_Transmit(&huart1, (uint8_t*) buffer, strlen(buffer),
// HAL_MAX_DELAY)
snprintf(buffer, sizeof(buffer)," Weight: %.f g\n\r",get_wieght);
HAL_UART_Transmit(&huart1, (uint8_t*) buffer, strlen(buffer),HAL_MAX_DELAY);
}
}
this hx711 fun for measuring the weight

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 1024);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

/* definition and creation of myTask02 */
osThreadDef(myTask02, StartTask02, osPriorityNormal, 0, 512);
myTask02Handle = osThreadCreate(osThread(myTask02), NULL);

/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */

/* Start scheduler */
//osKernelStart();

/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */

/* Start scheduler */
osKernelStart();

this the main
void StartTask02(void const argument) {
/
USER CODE BEGIN StartTask02 /
/
Infinite loop */
ValveOperatingTask();

for (;;) {
	osDelay(1);
}
/* USER CODE END StartTask02 */

}
here i am calling my valve fun
void StartTask02(void const argument) {
/
USER CODE BEGIN StartTask02 /
/
Infinite loop */
WeightMeasurementTask();

for (;;) {
	osDelay(1);
}
/* USER CODE END StartTask02 */

}
and here i am calling the hx711 fun for weight measruing

Try to separate the valve power wires from rest of the wiring and devices as well as possible.
RC-snubber across the valve could help as well.
And already mentioned shielded wiring for sensors.

I also tend to use shielded cables from the microcontroller relays to the motors, actuators, valves, contactors (e.g. to switch three phase motors), etc

also be very careful about the ratings of relays commonly used on microcontrollers such as Arduinos
relays often state 10amp switching capacity with no indication if it is AC1, AC3, etc
if a relay has 10amp printed on it with no other information I would regard this as a resistive load reducing it to 2 or 3amps for an inductive load

Now I am providing separate power to the relay using a 5V DC SMPS, and a separate power supply to the HX711 module. The ground is common between the SMPS, the HX711 power supply, and the MCU.

I am only using the relay module to switch the AC valve, and the HX711 amplifier module to communicate with the MCU for load cell readings. The load cell wires are also shielded.

Power is completely separated for both modules — the relay and the HX711 amplifier. To protect against EMI, I have also added a snubber circuit across the relay contacts.

However, I am still getting a thread blocking issue when the AC valve switches. The task responsible for reading data from the HX711 and sending it over UART stops working.

I have also attached a screenshot of the relay module for reference

Sir, I am only using a relay module for switching the AC solenoid valve, and an HX711 amplifier module to interface the load cell with the MCU. I have provided separate power supplies for the relay module and the HX711 module.

The ground wires of both power sources are connected to the MCU (common ground). The load cell wires are also shielded to reduce noise.

I have attached a screenshot of the relay module for your reference

You don't mention if the AC valve wiring is running together with other wires or nor.
You obviously tested the relay switching without valve connected?

1 Like

I would assume the relay in the photo is maximum 10amp resistive load
for an inductive load 2 to 3 amps maximum

the relay is labeled 5volt - the STM32 uses 3.3V logic - how do you drive the relay?

I am using an extension board. I have plugged the 5V DC SMPS into it, and I am also plugging the AC valve wires into the same extension board

Sir, I am supplying 5V DC to the relay from a 5V DC SMPS. I have connected the relay’s DC- and DC+ pins to the SMPS, and the signal pin of the relay is connected to the STM32 development board

It would be more correct to remove the jumper and connect the center pin to your MCU 3.3V supply.

Yes, I have tested the relay without connecting the AC valve, and it switches perfectly.
Which jumper wires are you talking about? Do you mean the jumper wire on the relay module

Jumper, not wire.

But without connecting the AC valve, the relay switches perfectly. However, when I connect the AC valve and power it through the relay, I start getting the thread blocking issue

Snubber, shielded wires and carefully organized wiring is often enough.
Powering the relay without sharing gnd with MCU likely doesn't resolve your problem, but it's more correct anyway.

You could post a photo of your setup, maybe someone catches something.

Put the snubber across the valve coil, not the relay contacts.