Recap:
I'm building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master's thesis of a minute Shape memory Alloy based solar panel cleaning robot
Previous posts:
- SolarSense - Part 1 - Introduction, The POC Built and The Plan
- SolarSense - Part 2 - Can Arduino CAN?
- SolarSense - Part 3 - PCB Schematics Walkthrough
- SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation
- SolarSense - Part 5 - Making an Live Dashboard using Lab View
- SolarSense - Part 6 - Reading the Heat at K Type Thermocouple using MAX31855.
- SolarSense - Part 7 - Reading the Sky - UV Sensor Interface on STM32
Time is short, So i will jump straight into the points and avoid good formatting of this post. I have SEN66, BMP280 in my board and yet why am i using another sensor for Relative Humidity and Temperature? I needed something that could live away from the PCB, immune to internal heats etc, a probe on a cable, pointed at the actual outdoor air, unaffected by what is happening inside the enclosure. The Omega HX94C is exactly that. It is an industrial-grade transmitter, NEMA 4 rated, with two completely independent 4-20 mA current loops: one for relative humidity (0–100 %RH at 4–20 mA) and one for temperature (0–100 °C at 4-20 mA). You run a pair of wires out to it, excite the loop from your rail voltage, and measure the current. The Current based measurement means, length of wire will not matter, as opposed to voltage based measurement where the voltage will drop along the wire.


I am using a TPS61040DBVT boost converter to get a 12V, HX94C requires excitation voltage of 6-36V for its working. Next is choosing the shunt resistor to measure the full range of 4-20mA. At 150Ohm ressitor
At 4 mA: V = 0.004 × 150 = 0.60 V
At 20 mA: V = 0.020 × 150 = 3.00 V
This is within the range of STM32's ADC
The Firmware
Just like my previous post of the UV sensor, I am simply reading the ADC values for both using
static HAL_StatusTypeDef adc_read_mv(HX94C_t *dev, uint32_t channel, uint32_t *mv)
{
ADC_ChannelConfTypeDef c = {0};
c.Channel = channel;
c.Rank = ADC_REGULAR_RANK_1;
c.SamplingTime = ADC_SAMPLETIME_640CYCLES_5; /* high source impedance */
c.SingleDiff = ADC_SINGLE_ENDED;
c.OffsetNumber = ADC_OFFSET_NONE;
HAL_StatusTypeDef st = HAL_ERROR;
if (HAL_ADC_ConfigChannel(dev->hadc, &c) == HAL_OK &&
HAL_ADC_Start(dev->hadc) == HAL_OK)
{
if (HAL_ADC_PollForConversion(dev->hadc, ADC_TIMEOUT_MS) == HAL_OK)
{
uint32_t raw = HAL_ADC_GetValue(dev->hadc);
*mv = (raw * HX94C_VDDA_MV) / ADC_FULL_SCALE;
st = HAL_OK;
}
HAL_ADC_Stop(dev->hadc);
}
return st;
}\
Once I have the millivolt reading from the shunt, the math is straightforward:
/* I(µA) = V(mV) / Rsense(Ω) × 1000 */ int32_t i_rh = (int32_t)(rh_mv * 1000U / HX94C_RSENSE_OHMS); int32_t i_t = (int32_t)(t_mv * 1000U / HX94C_RSENSE_OHMS);
The results

The HX94C contributes six tokens: hx_ok (both loops healthy), hx_rh (tenths of a %RH), hx_t (centi-°C), hx_rhi / hx_ti (the two loop currents in tenths of a mA, kept for diagnostics) and hx_flt (the fault bitmask). Decoding the HX94C fields across those samples:
| hb | RH loop | RH | Temp loop | Temp |
|---|---|---|---|---|
| 51 | 15.5 mA | 71.7 % | 8.3 mA | 26.8 °C |
| 52 | 15.7 mA | 72.8 % | 8.4 mA | 27.8 °C |
| 53 | 15.0 mA | 68.7 % | 8.7 mA | 29.2 °C |
| 54 | 15.5 mA | 72.1 % | 8.3 mA | 27.0 °C |
BTW, hb is heartbeat that comes every 5second
Final Notes
It's time to submit project, and Will invest time in writing the final post. Feel free to ask questions if i missed anything.