Embedded HandbookApps
All sections › V. Debugging and quality
STM32F103, STM32F407 · level reference · 47 min read
Open in the interactive handbook

36. Typical problems: symptom → cause → fix

Every section of the handbook ended with "pitfalls" — here they are gathered together and rearranged. Not by topic, but by what the trouble looks like: you see the symptom, not the cause, and the search has to start from it. More than four hundred rows, and most devices that "don't work for no reason" fall into one of them.

The number in the last column is the section where it is covered in detail.

Terms used in the documentation

Term Meaning
troubleshooting finding and fixing faults
symptom symptom, the outward sign
root cause the underlying cause
intermittent occurring only "sometimes"
heisenbug a bug that disappears when you try to observe it
works on my desk "it works on the bench"
sanity check checking the obvious
bisect searching by halving

36.1 Order of actions

Before searching the table — five questions in order. A "no" to any of them means it is too early to search further.

1. Does it build without warnings? Not just without errors. A warning about an implicit conversion, an unused value or an uninitialized variable is the answer in half of the cases.

2. Does it reach main? A breakpoint on the first line. If it does not get there — wrong startup file, vector table, stack (section 11).

3. Is the thing that does not work clocked? Half of all "the code is correct, but it does not work" cases are a forgotten bit in RCC->AHBENR/APB1ENR/APB2ENR. Check the register in the debugger, not the code: the code may be written but never reached.

4. Is the frequency right? Read RCC->CFGR and SWS in the debugger instead of trusting SystemCoreClock. A crystal of the wrong frequency, a PLL that did not start, SWS not checked — and everything that depends on time is off proportionally.

5. Is the interrupt enabled at every level? Peripheral → NVIC → global. And the priority is assigned explicitly — by default it is zero, and under the kernel that is a bug.

Next comes the table.

Five questions in order and what to check when the answer is "no"

Three techniques that always work

Halve it. Remove half of the code — does it work now? Then the trouble is in the removed half. Repeat. Ten steps — a thousand lines.

Halving: a thousand lines narrow down to one in ten steps; one step is removing half of the code and checking whether it works

Reduce to the minimum. A separate ten-line project that reproduces the trouble. If it does not reproduce — the trouble is in the interaction, and that is already an answer.

Read registers, not code. The code says what you wanted. The register says what you got. If they differ — the problem is somewhere between them.


36.2 Does not build or link

Symptom Cause Fix §
a colleague gets file not found on a header of the same project an absolute path in Include Paths paths relative to the project file: .\App;.\Board;.\Drivers 4а
undefined symbol at link time, although the file is in the folder the file is not added to a µVision project group add the file to the group 4а
Error #540: … component is not available … pack … is not selected pack versions are not pinned, and the newest pack is organized differently pin the versions in Select Software Packs 4а
"uses RTOS support that is part of a Legacy Pack" the old RTX OS is enabled in the target of someone else's project in the target options choose Operating system: None 4а
SVC_Handler defined twice handler stubs remained in the interrupt file delete the three stubs; keep the name substitution in the configuration 24
unresolved reference to a kernel function that exists in the sources its INCLUDE_ is not enabled add the macro to FreeRTOSConfig.h 24, 33
unresolved reference to vApplicationGetIdleTaskMemory static allocation is enabled write both memory-supplying functions 24
#error from FreeRTOS.h a mandatory macro is not defined the message names it 33
error on pvPortMalloc no heap file is included add one heap_N.c 25
error on xPortGetMinimumEverFreeHeapSize heap_1/2/3 is included switch to heap_4 25
__forceinline does not compile the RVDS port used with Arm Compiler 6 take portable/GCC/ARM_CM3 24
software timers do not build no INCLUDE_xTaskGetSchedulerState or configTIMER_* add them 33
the firmware suddenly got heavier timers, groups, buffers are enabled — but only one is needed disable the extras 24
'else' without a previous 'if' after a macro call a multi-statement macro without do { } while (0) wrap the body in do { … } while (0) 127
after moving to C23 — "too many arguments to function call" a declaration f() without void: in C23 this is a function with no parameters write a prototype with parameter types 128
"unknown type name 'constexpr'" the standard is below C23 or the compiler has no C23 -std=c23 on armclang 6.24 or #define / enum 128
__stack_chk_fail not found at link time -fstack-protector-strong is enabled, but there is no handler and no guard word write __stack_chk_guard and __stack_chk_fail 129
code from F4 does not build on F0 or G0: __set_BASEPRI unknown Cortex-M0 and M0+ have no BASEPRI critical section through PRIMASK (__disable_irq) 7
on F0, after your own bootloader, interrupts go to its table Cortex-M0 has no VTOR copy the table to the start of RAM and set MEM_MODE in SYSCFG->CFGR1 7
undefined reference, although -l is given the library comes on the command line before the object that uses it -l after the sources and objects 143
relocation R_X86_64_32 … recompile with -fPIC an object for a .so was built without -fPIC rebuild with -fPIC 143
libpq-fe.h: No such file the libpq headers live in /usr/include/postgresql -I$(pg_config --includedir) 148
insmod: Invalid module format the module's vermagic does not match the kernel build against your kernel's headers 147
class_create does not build since kernel 6.4 it takes one argument, before that two branch on LINUX_VERSION_CODE 147
fatal error: esp_flash.h: No such file in an ESP-IDF project the component has REQUIRES — dependencies are now explicit only add the component to REQUIRES 151
ledc_timer_config returned ESP_FAIL frequency × 2^resolution exceeds 80 MHz lower the resolution or the frequency 153
the firmware built, but the image is 0 bytes and the board is silent your own linker script has no KEEP(*(.isr_vector)) and ENTRY, and --gc-sections threw everything out restore ENTRY(Reset_Handler) and KEEP, check --print-memory-usage after the build 4в

36.3 Does not start

Symptom Cause Fix §
does not reach main startup file, vector table, stack pointer a breakpoint in Reset_Handler 11
HardFault right after vTaskStartScheduler the names of the three handlers are not substituted the vPortSVCHandler macros and the others 24
builds, but tasks do not switch the same: the vectors lead to empty stubs the same 24
halt inside port.c at start-up configASSERT: interrupt priority or grouping call stack; PRIGROUP ≤ 3 24, 28
stuck in vPortValidateInterruptPriority an interrupt above the threshold called a kernel function priority ≥ 5 or remove the call 28
halt right after a task starts the task returned from its function an infinite loop or vTaskDelete(NULL) 26
out of memory from the very first run a 10 KB heap on a chip with 20 reduce it or use static allocation 25
the clock "resets" on every reboot the RTC is initialized unconditionally check the initialization flag in the backup domain 12
instant exit from Standby CWUF not cleared clear it before entering 10
cannot open shared object file at start-up the library is not in RUNPATH, LD_LIBRARY_PATH or the cache; the current directory is not searched rpath $ORIGIN or install it into the system 143
insmod: Operation not permitted no privileges, or a container root on a real machine 147
ESP32 does not start with peripherals connected a sensor pulls a strapping pin at reset; GPIO12 pulled up switches the flash to 1.8 V free pins 0, 2, 5, 12, 15 during reset 153
ESP32 reboots when Wi-Fi is turned on a dip below the 2.43 V threshold: a weak regulator or cable a supply with headroom for radio peaks 150
the module has 4 MB, but the program sees 2 the flash size in menuconfig defaults to 2 MB CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 150
ESP32 is back on the old version after an over-the-air update the new firmware restarted before confirmation — the bootloader rolled it back confirm with esp_ota_mark_app_valid_cancel_rollback() after a self-test 159
after flashing with a programmer, ESP32 boots the old application otadata still holds an entry for the ota_0 partition erase otadata or the whole flash memory 159

36.4 The peripheral is silent

Symptom Cause Fix §
ADC code from F1 does not build on F0 or G0: no SQR3, CR2 the F0/G0 ADC has no sequence — channels are marked with bits a mask in CHSELR, start with ADSTART, end with EOC 14
G0: the ADC never gives ADRDY, calibration never finishes the ADVREGEN regulator is not enabled or the 20 µs after it were not waited out ADVREGEN, a 20 µs pause, then ADCAL and ADEN 14
G0: after a channel change the first measurement is from the old channel the new CHSELR mask does not take effect immediately after writing CHSELR wait for CCRDY 14
G0: supply voltage via VREFINT reads 10 % high the factory code was taken at 3.0 V, while the formula uses 3300 VREFINT_CAL_VREF from the LL of your series 14
I²C code from F1 does not build on F0 or G0: no SR1, DR, CCR F0/G0 have a different I²C block TIMINGR, the exchange through CR2 (NBYTES, AUTOEND), flags in ISR 17
F0/G0: the program hangs waiting for TXIS nobody answered the address — NACKF arrived wait for TXIS and NACKF together, clear NACKF in ICR 17
F0/G0: after the first exchange the block is "busy" STOPF not cleared after AUTOEND write STOPCF to ICR 17
SPI code from F1 does not build on F0 or G0: no DFF in the new SPI the data size is the DS field in CR2 DS = size − 1, 0111 for 8 bits 18
F0/G0: SPI sends an extra zero byte, reception is shifted by a byte a 16-bit write to DR with 8-bit frames write and read DR as a byte: *(volatile uint8_t *)&SPI1->DR 18
F0/G0: RXNE does not rise after one byte the receive FIFO threshold is two bytes the FRXTH bit in CR2 18
G0: the DMA channel is configured and enabled, but nothing is transferred the request is not routed to the channel — DMAMUX not written DMAMUX1_ChannelN->CCR = request number from LL; DMA channel N+1 ↔ DMAMUX channel N 9
DMA code from F1 does not build on F0 or G0: DMA_CCR4_DIR, DMA1EN the CCR bits have no channel number, on F0 the clock bit is DMAEN DMA_CCR_DIR, RCC_AHBENR_DMAEN 9
F0: DMA from USART1 goes to the wrong channel the mapping differs from F1: USART1_TX is channel 2, with USART1TX_DMA_RMP — 4 the F0 mapping table or the remap bit in SYSCFG->CFGR1 9
sleep code from F1/F0 does not build on G0: no PWR->CR, PDDS on G0 the mode is the LPMS field in CR1, the flags are in SCR LL_PWR_MODE_* in LPMS, CWUF1/CSBF in SCR, EWUP1 in CR3 10
G0: a Flash write gives SIZERR or PGAERR SIZERR — a byte or halfword write; PGAERR — two words not from the same double word double words at an address that is a multiple of 8: two 32-bit words in a row 11
Flash code from F1/F0 does not build on G0: no FLASH->AR on G0 a page is erased by its number the PNB field in CR 11
timer code from F1/F0 does not build on G0: no RCC->APB1ENR on G0 the clock registers are APBENR1/APBENR2 RCC_APBENR1_TIM3EN, RCC_APBENR2_TIM1EN 13
F0/G0: the PWM frequency is lower than calculated — by 1.5 times (F0) or by 1.125 times (G0); by 9 or 4.5 times if the system stayed on HSI PSC calculated for the 72 MHz of F1 timer clock 48 (F0) or 64 MHz (G0): PSC 47 or 63 for a 1 MHz count; check that the system moved to the PLL 13
the code is correct, the register does not change the block's clock is not enabled the bit in RCC->xxxENR; check it in the debugger 6
HardFault with PRECISERR, BFAR = a peripheral address the same: access to an unclocked block the same 6, 35
a write to PWR->CR has no effect PWR on APB1 without a clock RCC_APB1ENR_PWREN 10
the timer counts, but nothing on the pin the pin is not in alternate function mode configure the GPIO 13
timer settings do not take effect no update event the UG bit or wait for an overflow 13
TIM1 is silent MOE in BDTR is not set set it 13
the timer interrupt does not arrive HAL_TIM_Base_Start only starts the counter, UIE is not set HAL_TIM_Base_Start_IT 13
the BluePill LED does not respond to PWM PC13 is not connected to a timer an external LED on a channel pin, for example PA6 13
a table does not go to PWM through DMA the pacing timer does not have the UDE request enabled DIER = TIM_DIER_UDE 9
DMA takes every second value of the table MSIZE is 32 bits with a uint16_t table sizes by element and by register 9
the interrupt does not arrive the chain: port clock → EXTI → NVIC → global check along the chain 7
the handler is called endlessly the EXTI->PR flag is not cleared write a one 7
I²C does not work at all no pull-ups 4.7 kΩ on both lines 17
I²C: conflict with the slave push-pull pins open-drain 17
SPI does not respond CPOL/CPHA mode, MOSI/MISO, CS, speed check in order 18
SPI drops out of master mode SSI not set set it together with SSM 18
CAN does not receive although transmission works no filter is configured at least one that accepts everything 19
a lone CAN node repeats a frame endlessly nobody acknowledges: the node is passive but not in bus-off a second node or loopback 19
USB is not detected D+ pull-up, 48 MHz, HSI instead of a crystal 1.5 kΩ resistor; crystal; frequency 20
CAN and USB together on F103 shared buffer memory one or the other 19, 20
SWO is silent SWD, not JTAG; TRACE_IOEN; TRCENA; the key; the channel; debugger settings in order 21
SWO does not work on a clone debugger the clone does not accept trace a different debugger 21
PB3 does not work as GPIO it is taken by SWO one or the other 21
DMA does not start the enable on the peripheral side is forgotten (DMAT, DMA in ADC_CR2) set it 9
DMA on F4 does not start a second time flags from the previous transfer remain clear them in IFCR 9
there is no DAC in the chip it simply is not there read the datasheet before layout 15
the watchdog fires while debugging DBGMCU->CR is not set stop the watchdog in debug 12
SPI flash is written "in vain" Write Enable forgotten before every write and erase 41
f_mount → FR_NO_FILESYSTEM the medium is not formatted f_mkfs 42
gpio_set_direction returned ESP_ERR_INVALID_ARG pins 34–39 are input only, numbers 24 and 28–31 do not exist take another pin 153
ADC2 answers ESP_ERR_TIMEOUT with Wi-Fi on, the second ADC block is taken by the radio move the input to ADC1 (GPIO32–39) 154
adc_continuous_config → ESP_ERR_INVALID_ARG the rate is below 20 kHz or ADC2 is selected 20 kHz … 2 MHz and ADC1 only 154

36.5 Works, but not right

Symptom Cause Fix §
all delays are wrong, but proportionally configCPU_CLOCK_HZ ≠ the real frequency make it match RCC 33
the timer frequency is twice as high the clock doubling on APB1 when the prescaler ≠ 1 account for the ×2 13
blinks twice as slow as calculated toggling every 0.5 s gives a period of 1 s blink period = two toggles 13
brightness jumps to maximum when the encoder is turned left CNT was taken as a position and wrapped through zero a signed difference of readings, an accumulator with limits 13
the DAC/signal frequency is wrong the same doubling the same 15
delays got shorter after changing the tick delays are written as plain numbers pdMS_TO_TICKS everywhere 24
the task period is longer than specified vTaskDelay counts from the call xTaskDelayUntil 26
two tasks with different priorities behave as equals a priority above the maximum is silently clamped ≤ configMAX_PRIORITIES − 1 26
the stack headroom looks four times smaller the function returns words ×4 26
vTaskList shows a waiting task as suspended the report takes an indefinite wait for a notification for a suspension this is normal; eTaskGetState returns eBlocked 26
the counter counts less a race on the increment a critical section 30
mutex: Give returns a failure the mutex was not taken; for a recursive one — it is given by a non-owner every Give has its own Take in the same task 30
a stopped timer started by itself xTimerChangePeriod starts it xTimerStop right after 29
a mask-driven output does not turn off when the condition disappears the code waited for group bits, but the group does not report a bit being cleared wait for a change indication, evaluate the rules yourself 31
outputs are computed from "stale" inputs the group was re-read after waking up parse the value that xEventGroupWaitBits returned 31
task shares in the run-time statistics jump around the counter ticks less often than tasks switch DWT->CYCCNT or a fast timer 26
the chip stops when a task is created configASSERT on a priority not less than configMAX_PRIORITIES a priority below the maximum 26
a device with a hung task does not reboot the watchdog is fed from a software timer without checks: the daemon is always alive feed only when all tasks have checked in 29, 12
the button-polling task "sticks" xQueueSend waits for space in a full queue wait 0 and count the losses 27
one press gives several events contact bounce three identical reads in a row 27, 8
the timer fires late the daemon priority is below a busy task daemon at maximum 29
cannot get finer than a millisecond the resolution is the tick a hardware timer 29
a task wakes up in a loop without an event the group flag is not cleared on exit clearOnExit = pdTRUE 31
a flag set from an interrupt arrives with a delay it goes through the timer daemon enable timers, raise the daemon 31
event group bit 24 does not work the top byte is used by the kernel bits 0…23 31
the task wakes up on every byte trigger level 0 or 1 a sensible threshold 32
the ADC is off after overclocking ADCCLK exceeded prescaler 14
ADC readings are low and drift sampling time too short increase SMPR 14
ADC: only the first channel is read scanning without DMA enable DMA 14
ADC: error on F1 no calibration CAL after power-up 14
the DAC does not reach the rails the output buffer is enabled this is normal; or disable it 15
the DAC sags under load the buffer is disabled, the load is low-impedance enable the buffer 15
the DAC signal is stepped too few points or no filter table + RC 15
UART: garbage instead of text baud rate or divider recalculate BRR 16
SWO: garbage instead of text the core frequency in the debugger ≠ the real one correct it in the settings 21
SWO loses characters the speed is too low for the volume raise the speed, print less 21
SPI: shifted by a bit wrong phase CPHA 18
CAN: wrong bit rate timing fields not reduced by one −1 in each field 19
CRC continues the previous calculation not reset CRC_CR_RESET 12
the watchdog timeout is cut too fine LSI drifts by tens of percent double margin 12
DS18B20 returns exactly +85 °C read before the conversion finished, or the sensor restarted from a power dip wait the full conversion time; 85.000 — measure again 96
frost turns into thousands of degrees the DS18B20 code was read as unsigned cast to int16_t 96
DHT22 is silent the start pulse is short or it is polled more often than once every 2 s 2 ms (DHT11 — 18 ms), not more often than once every 2 s 97
DHT22 shows thousands of degrees in frost sign-and-magnitude was read as two's complement the top bit is the sign 97
the SHT31 CRC never matches the 1-Wire CRC function was used polynomial 0x31, initial value 0xFF, most significant bit first 97
pressure is meaningless with a correct circuit the BMP280 calibration was not read or was parsed with the wrong byte order 24 bytes from 0x88, least significant first 98
BME280 gives no humidity the module has a BMP280: register 0xD0 holds 0x58 check the chip ID before use 98
barometric altitude drifts by hundreds of meters sea-level pressure changes with the weather: 30 hPa is 245.1 m p0 from a weather service or measure relative to the start 98
MPU-6050 returns only zeros it sleeps after power-up write 0 to PWR_MGMT_1 (0x6B) 99
ADXL345 readings are garbage with a correct circuit parsed most significant byte first, as on the MPU-6050 ADXL345 sends the least significant byte first 99
the gyroscope angle drifts by tens of degrees per minute the zero offset is being integrated calibrate the zero at rest, correct with the accelerometer 99
HC-SR04 distance reads a few percent high in frost temperature is not accounted for: a meter at 0 °C reads as 1037 mm include temperature in the echo conversion 100
the HC-SR04 timeout fires on every measurement without URS, resetting the counter on the echo edge raises the update flag TIM_CR1_URS 100
HC-SR501 does not see a sitting person the sensor detects motion, not presence account for the hold time or use a different sensor 100
INA219 shows zero current and power the calibration register was not written: it is empty after power-up write CAL at start-up 101
ACS712 shows current with no load zero was taken as half scale, but the sensor supply is not exactly 5 V: at 4.9 V — −268 mA measure zero with no load 101
the INA219 reading "froze" at high current more than ±320 mV across the shunt — the shunt register hit ±32000; the OVF bit may not be set check the shunt register; a smaller shunt or a different gain 101
empty scales show 39 kg the 24-bit HX711 code without sign extension: −1 became 16777215 sign-extend 102
after changing the HX711 gain the first sample is "wrong" the gain applies from the next sample discard the first sample 102
the scales occasionally show wild values an interrupt stretched a high level on SCK beyond 60 µs — the HX711 went to sleep disable interrupts for the pulse burst 102
the photoresistor shows 0 or 4095 not light, but an open or short in the divider check the divider; the extreme codes mean a fault 103
BH1750 in the sun froze at 54612 lx the code hit 65535 at MTreg 69 MTreg 31 103
the MQ sensing layer resistance is a third higher the ADC divider sits in parallel with RL: 7.5 kΩ instead of 10 calculate with RL loaded by the divider 103
−250 °C from MAX31855 reads as 3846 °C a 14-bit code without sign extension sign-extend 104
MAX6675 readings do not change CS is pulled low more often than once every 0.22 s: the read interrupts the conversion read once every 250 ms 104
a thermocouple in a furnace shows 1184 instead of 1200 °C the linear MAX31855 conversion at 41.276 µV/°C correction from the type K table 104
Ethernet frames go out, but nobody answers the type was written least significant byte first: 0x0608 instead of 0x0806 numbers in frame fields go most significant byte first 105
the device sees its neighbors, but not the internet no gateway, or the mask is wider than the real one: a foreign address is looked up via ARP mask and gateway as in the network 105
ARP answers, but ping is silent a wrong IP or ICMP checksum — the receiver silently drops the packet compute the checksum with zero in its field 105
a TCP connection is "alive" 15 minutes after the cable was cut retransmissions with doubling: 924.6 s until failure your own link check 106
a device behind NAT stopped receiving commands the NAT entry expired: for UDP — after 30 s of silence keep-alive more often than the timeout 106
TCP tops out at hundreds of kilobits on a fast link a small window: 2048 bytes at 50 ms — 0.33 Mbit/s a bigger window or a shorter round-trip time 106
the device does not get an address via DHCP the "broadcast reply" flag is not set: the reply went to an address the device does not have yet flag 0x8000 in DHCPDISCOVER 107
the device goes to the server's old address the DNS answer is cached longer than the record's lifetime cache no longer than the TTL 107
TLS fails certificate verification the device clock is set to the year 2000: the certificate is "not yet valid" synchronize the clock before TLS 107
the reply from another network does not arrive, although the router passed the request the recipient has no return route: the reply went to its default gateway a route back through the same router 108
small packets go through the VPN, but large ones are lost the tunnel MTU is 1420: a 1500-byte packet does not fit MTU 1420, MSS 1380 108
a neighbor's Wi-Fi on channel 3 interferes more than on channel 1 adjacent channels overlap: 5 MHz spacing with a 20 MHz width channels 1, 6 or 11 108
Ada firmware: "cannot find entry symbol _start", image at 0x8000 the generic light runtime has no memory map of the chip your own crt0.S and link.ld or a runtime for the board 113
the Ada build fails on warnings about Last_Chance_Handler -gnatwe turns check reminders into errors keep the checks, remove -gnatw.X 113
gnatprove "proved" everything without a precondition it took old results from the same directory run in a clean copy 113
the Rust vector table is a kilobyte out of nowhere no chip crate: cortex-m-rt reserves all 256 words a chip crate with its interrupts 114
Cargo built the program, but not for the chip --target was forgotten --target thumbv7m-none-eabi or thumbv7em-none-eabihf 114
the crate built differently on another machine Cargo.lock is not committed Cargo.lock into the repository 114
PlatformIO: built yesterday, not today the platform is not pinned and got updated ststm32@20.0.0 in platformio.ini 115
CMake fails the compiler check for a bare-metal chip the trial build tries to link a program CMAKE_TRY_COMPILE_TARGET_TYPE = STATIC_LIBRARY 115
the PlatformIO compiler turned out to be old the default toolchain of ststm32 20.0.0 is GCC 7.2.1 your own package in platform_packages 115
the model on the board answers differently than on the computer features are not scaled as in training mean and spread go into the firmware together with the weights 116
k-NN does not fit in RAM it stores the whole training set: 600 examples — 4800 bytes of features a threshold, a tree or a small network 116
100 % on the training set, below the threshold on the validation set the model memorized the examples accuracy only on data the model has not seen 116
a peak in the vibration spectrum that the mechanism does not have aliasing: 160 Hz sampled at 256 Hz shows up as 96 Hz a sampling rate with margin and a filter before the ADC 117
the firmware grew by 77 KB from a single FFT arm_rfft_fast_init_f32 pulls in tables of all sizes arm_rfft_fast_init_256_f32 — for your own size 117
the bearing is recognized one time and not the next the "main peak frequency" feature: a strong rotation tone masks the defect energy in the defect frequency band 117
the model is 83 % correct in Python, much worse on site windows of the same recording ended up in both training and validation split by recordings: the honest estimate is 75.9 % 118
it finds almost no defects despite high overall accuracy a rare class: 21 windows out of 861, the model gains by ignoring it class weights: 180 out of 180 instead of 60 118
the model considers a healthy motor unbalanced the sensor mount has its own sensitivity calibration at installation: 410 → 481 out of 540 118
int8 outputs on the board differ by one from TensorFlow Lite CMSIS-NN by default rounds twice when requantizing: only 533 out of 540 windows matched CMSIS_NN_USE_SINGLE_ROUNDING — all 540 bit for bit 119
after quantization the model answers with a single class the input was quantized not with the input tensor's scale and zero point, or without normalization scale and zero point from the .tflite, normalization as in training 119
a strong signal cannot be told from a very strong one saturation: the representative dataset never saw such amplitudes all classes and modes in the representative dataset 119
the normal-behavior model does not see increased vibration the shared baseline includes all motors: imbalance at a 1 % threshold found in 45 windows out of 400 the device's own baseline after installation — 400 out of 400 120
more false alarms than the threshold promised the threshold was chosen on other motors: 49 out of 1200 instead of 1 % its own baseline or a threshold from the first hours of operation 120
the device got used to a growing defect the baseline adapts to new windows retrain the baseline only on command, keep the score history 120
the state-of-charge estimate drifts on an old battery a filter with the datasheet R0 attributes the extra voltage drop to charge: 5.6 % R0 in the filter state: 0.7 % 121
added a state to the filter — it got worse the current offset is poorly observable: estimated 0.085 A instead of 0.05 do not add states the measurement cannot see 121
charge from voltage under load is off by tens of percent the voltage was measured with the I·R0 drop and the RC network the table only after rest, under load — the filter 121
the released firmware version has -dirty built from uncommitted changes, the build cannot be repeated release only from a clean tree, version from git describe 109
firmware 1.9.3 is considered newer than 1.10.0 versions are compared as text compare as numbers: major, minor, patch, commits after the tag 109
unknown which commit broke the build going through twenty commits by hand git bisect run: commit 13 out of 21 in 5 checks 109
the firmware image differs every time it contains the build time, directory paths and the build ID SOURCE_DATE_EPOCH, -ffile-prefix-map, --build-id=none — the same image byte for byte 110
a test failed, but the release went out anyway the pipeline does not check the exit code the stage fails on any failed check, no artifact is created 110
the build suddenly changed without code changes the "latest version" compiler updated itself the compiler version is pinned in the build image 110
a timeout fires early after a long run comparison with start + timeout, which overflowed: after 8 ticks instead of 100 (uint32_t)(now - start) >= timeout 122
the check ~x == 0xF0 for uint8_t is always false promotion to int: ~0x0F is -16 (uint8_t)~x == 0xF0 122
the comparison is right on the computer, wrong on the board a different data model: (long)-1 < 1u gives 1 on x86-64 and 0 on Cortex-M types from stdint.h, explicit casts, _Static_assert 122
works with -O0, breaks with -O2 undefined behavior: signed overflow, strict aliasing a build with UB traps and -Wall; bits through memcpy 123
an overflow check disappeared in the release build the check comes after the operation: the compiler replaced x + 1 > x with 1 check before the operation or use __builtin_add_overflow 123
HardFault on a UDF instruction in debug firmware an undefined-behavior trap fired the address in the exception frame is the faulty line 123
a loop waiting for a flag from an interrupt does not wait or hangs the flag is not volatile: armclang turned the wait into BX lr volatile on the flag 124
an event counter from an interrupt sometimes lags events++ is LDR, ADDS, STR, with an interrupt between them atomic_fetch_add (LDREX/STREX) or a critical section 124
tables ate up the RAM without const a table is copied from flash memory into .data const — the table stays in flash memory 124
HardFault when calling a handler from a table a null pointer or a table in RAM corrupted by an out-of-bounds write index and pointer checks, a const table in flash memory 125
a command is not found in the table, although it is there the table for bsearch is not sorted by name a table-order test on the computer 125
HardFault on Cortex-M0 while parsing a packet (uint32_t )(buf + 1) is an unaligned access memcpy or byte-wise parsing: the compiler chooses LDRB itself 126
the receiver sees garbage in the packet a struct with padding bytes was sent serialize the fields byte by byte 126
numbers from the sensor are byte-swapped the sensor sends the most significant byte first, Cortex-M stores the least significant first byte-wise parsing with an explicit order, or REV 126
the error message belongs to the neighboring code the enum and the message table are written separately an X-macro from a single list or _Static_assert on the table length 127
garbage in the terminal after changing the bus frequency the UART divider was not recalculated, the baud error exceeds 2 % check the divider in #if with #error at build time 127
a task corrupts someone else's memory on a long packet a variable-length array on the stack: the size is taken from the packet -Wvla, an array of the maximum size with a length check 128
a crash after a long packet, a return from a function into nowhere memcpy into a local buffer without a length check overwrote the stack frame a length check before copying, -fstack-protector-strong 129
the state machine froze after a fault a corrupted state variable, the switch has no default default — go to a safe state 129
on G0 a falling-edge interrupt loops forever the flag is cleared in PR as on F4, but on G0 a falling edge is flagged in FPR1, a rising edge in RPR1 clear it by writing a one to FPR1 or RPR1 7
on G0 the button is always "pressed", IDR reads zero after reset G0 pins are analog (MODER = 11), the digital input is off switch the pin to input: MODER = 00 8
code for F0 does not build on G0: GPIO_MODER_MODER5 unknown the G0 header names the bits differently (MODE5, BS5, ID0) masks by pin number or LL functions 8
on G0 it does not switch to the PLL, although PLLRDY is set output R is not enabled with the PLLREN bit set PLLREN together with the dividers in PLLCFGR 6
on F0 or G0 the UART went silent after the first overrun the ORE flag is not cleared by itself, as on F1 by reading SR and DR clear it by writing USART_ICR_ORECF to ICR 16
device readings disappeared after a server update data was written inside the container, not into a volume everything that must outlive the container goes into volumes; down -v only with a backup 111
in the receiver log all devices have the same address it is the reverse proxy's address the proxy sets X-Real-IP, the receiver takes the address from it 111
the proxy answers 502 right after the stack starts the receiver is not ready yet, the proxy started earlier healthcheck and condition: service_healthy 111
the server disk filled up with container logs the json-file driver has no size limit max-size and max-file in logging 112
the telemetry receiver went down, but there are no alerts the rules are only about devices: without metrics they stay silent an up == 0 rule for the receiver itself, the alert channel as a separate service 112
the on-call engineer is woken up by single sensor spikes the alert rule has no for the condition must hold for a set time 112
Cyrillic file names are garbage no FF_USE_LFN, the code page, or UTF-8 strings with FF_LFN_UNICODE = 0 enable it, FF_CODE_PAGE, FF_LFN_UNICODE = 2 42
slow writing to flash through FatFS a 512 sector → read-erase-write buffer, use larger blocks 42
statistics: printf stopped working formatting functions need stack more stack or disable it 33
a line printed before fork appeared twice the stdout buffer was copied into the child fflush(NULL) before fork 136
read returned −1 for no visible reason EINTR: a signal interrupted the call retry or SA_RESTART 136
read from a pipe never returns 0 the write end is open in the reader itself close it after fork 137
syslog writes nothing no /dev/log in a container or a minimal system LOG_PERROR or your own file 138
read returned −1 on a non-blocking socket EAGAIN means "nothing yet", not an error wait for readiness via poll/epoll 141
epoll with EPOLLET misses data the socket was not read until EAGAIN read until EAGAIN 141
a thousand inserts into SQLite take seconds autocommit on every one BEGIN … COMMIT in a batch 148
"code × 0.806 mV" on ESP32 is off by hundreds of millivolts the reference is not 3.3 V, the line does not pass through zero adc_cali_raw_to_voltage with calibration 154
ledc_set_duty changes nothing the duty cycle is applied by a separate call add ledc_update_duty 153
fopen("/fat/лог.txt") returned NULL FAT long names are disabled, only 8.3 enable LFN in menuconfig or use 8.3 names 155
files in SPIFFS take three times more than the data a 256-byte page and an index for every file large files or FAT 155
printf("%.2f") prints nothing in newlib-nano (--specs=nano.specs) float output is disabled to save size the -u _printf_float option (+13 KB of code) or print integer parts 4б

36.6 Works sometimes

The nastiest class. The general rule: "sometimes" is most often a race, timing or memory. Among hardware causes — long wires, power and ground: these are examined with an oscilloscope, not a debugger.

Five signs of an intermittent fault and their usual causes: only with the debugger — a race, with minus O2 optimization — missing volatile or undefined behavior, after an hour — an ordinary kernel function from an interrupt, after a week — a memory leak, works on the bench but fails in the enclosure — wires, power and ground, examined with an oscilloscope

Symptom Cause Fix §
works only with the debugger a race: the timing under the debugger is different volatile, critical sections, waiting for flags 3, 16
works at -O0, breaks at -O2 missing volatile or a race it is not a "compiler bug" 3, 30
after an hour — data corruption or a hang an ordinary kernel function from an interrupt only FromISR 28
the device crashes after a week a memory leak watch the minimum-ever free heap 25
memory melts away as tasks are created and deleted the idle task does not get the CPU give it time 26
tasks get created, then stop being created the heap ran out xPortGetMinimumEverFreeHeapSize 25
plenty is free, but nothing can be allocated fragmentation heap_4, create and delete less often 25
a high-priority task stalls for no reason priority inversion a mutex, held briefly 30
a task misses events a binary semaphore with frequent events a counting one or a queue 28
the task reaction lags by a millisecond portYIELD_FROM_ISR forgotten add it 28
a critical section does not protect a variable the variable is touched by a fast interrupt different protection or below the threshold 28, 30
UART reception loses bytes while the display is in use a long critical section a mutex or a gatekeeper 30
UART bytes are lost under load the buffer is full a bigger buffer, a higher reader priority 32
data in the buffer gets corrupted two writers or two readers the single-writer, single-reader rule 32
notification values are lost it holds only one a queue 31
bounce gives a dozen interrupts the button mechanics debouncing 7
UART: the first byte is lost the receiver is enabled later than the transmitter the enabling order 16
UART: reception is silent after a stream ORE not cleared read SR, then DR 16
UART: the end of a transmission is cut off waited for TXE instead of TC TC before sleep 16
SPI: data is cut off at speed CS released before BSY cleared wait for BSY 18
USB: works on one computer, not on another the pull-up or the line quality resistor; cable 20
USB: drops out after sleep it requires activity wake-up handling 20
USB: data is lost when sending fast the host polls on a schedule a buffer and sending when ready 20
a command over USB CDC is not recognized the packet was compared with the command, but it arrived in pieces a ring buffer and assembling lines in a loop 20
binary data over CDC is cut off strncpy stopped at a zero byte copy bytes, not strings 20
a CDC reply is corrupted the buffer was changed before the transfer finished a separate buffer, send after USBD_OK 20
SPI flash works every other time on a breadboard long wires at 20 MHz lower the speed, shorter wires 41
I²C hangs forever no timeouts; a wire came loose timeouts everywhere 17
the stack runs out on printf hundreds of bytes for formatting headroom or drop printf 26
stack overflow in the timer daemon printf or an array in a callback do not do that there 29
a zero-length file after power-off no f_close/f_sync f_sync after writing 42
RTC time on F1 occasionally jumps by 18 hours the counter was read in halves without a retry — the low half wrapped through 0xFFFF between reads re-read the high half and retry 12
a counter from threads does not add up, differently every time a race: ++ is not atomic, volatile does not help a mutex or atomic_fetch_add 139
a counter in shared memory counts wrong no semaphore between processes a POSIX or XSI semaphore 137
a signal "got lost" standard signals are not queued block them with a mask instead of losing them 136
a counter from two ESP32 tasks does not add up, although there is a mutex the second core: the mutex does not protect against it a portMUX_TYPE spinlock or __atomic_* 152
a clock input on a long wire catches extra edges ringing of an unmatched line: the wave doubles at the end and reflects a series resistor at the pin up to the line's characteristic impedance 160
I²C works on the bench, fails on the wires in the enclosure the wire capacitance slows the R·C edge beyond the mode's limit a smaller pull-up, shorter wires, standard mode or a bus buffer 160

36.7 Hangs

Symptom Cause Fix §
hangs, but not completely: part of it works a high-priority task without waiting add blocking 26
alive, but does nothing a deadlock of two mutexes a single lock order 30
stuck on xSemaphoreTake of its own mutex it took it twice sort out the calls; a recursive one 30
all software timers stopped at once a callback blocked zero wait in callbacks 29
hangs without the debugger SWO waits for readiness with the module disabled check ITM_TCR_ITMENA 21
hangs waiting for RCC readiness the crystal did not start, there is no timeout a timeout; check the crystal 6
the handler waits on a semaphore you cannot wait in an interrupt TakeFromISR without waiting 28
xTimerStart returns a failure the command queue is full or the daemon is stuck see "all timers stopped" 29
task synchronization hangs SetBits + WaitBits instead of Sync xEventGroupSync 31
xMessageBufferReceive always returns zero the receive buffer is smaller than the message, which got stuck receive buffer ≥ the maximum frame 32
buffer Reset fails someone is waiting stop the reader 32
flash erase "hung" hundreds of milliseconds are normal wait for BUSY with a timeout 41
the device does not sleep in tickless mode a task wakes up more often than every two ticks find it 33
tickless mode is on, but the current did not drop configPRE_SLEEP_PROCESSING zeroes the argument before calling your function pass the time as is or go to sleep inside the function yourself 33
after vTaskSuspend the task is dead only vTaskResume brings it back do not confuse it with waiting 26
open of a FIFO for reading hangs there is not a single writer O_NONBLOCK or open it for writing yourself 137
cond_wait — sleeping forever called without the mutex held or without a condition-check loop while (!ready) cond_wait under the mutex 139
cat /dev/ringdev does not finish the driver's read does not return 0 on an empty buffer return 0 — end of file 147
Task watchdog got triggered … IDLE1 a task on core 1 did not yield the CPU for longer than 5 s vTaskDelay in the loop or subscribing to the watchdog 152
Interrupt wdt timeout on CPU0 interrupts were disabled for longer than 300 ms: an overlong critical section shorten the section 152

36.8 Memory and HardFault

Symptom Cause Fix §
PC in garbage, UNDEFINSTR the stack overwrote the return address measure the stack; a trap 35, 26
PRECISERR, BFAR is a small number a null pointer with an offset look for NULL 35
IMPRECISERR, PC points elsewhere the write went into the bus buffer DISDEFWBUF while debugging 35
UNALIGNED uint32_t * on a byte buffer memcpy or packing 35, 3
HardFault in a handler with priority 0 a handler above the syscall threshold calls the kernel priority ≥ the threshold 7, 28
the DMA buffer "is not filled" on F4 the buffer is in CCM, DMA cannot reach it ordinary SRAM 9, 41
DMA corrupts the stack the local buffer disappeared along with the frame a static buffer 9
data from DMA does not change in the code no volatile add it 9, 14
corruption with heap_5 the regions are not in ascending address order sort them 25
a static stream buffer corrupts its neighbor an array of N instead of N + 1 + 1 32
FATFS declared locally the structure must live all the time static 42
stack overflow on a sector buffer 4 KB locally a static buffer 42
the DMA buffer is not aligned a 16/32-bit element at an odd address __attribute__((aligned)) 9, 42
flash: wrote one thing, read another writing without erasing: only 1→0 erase the sector 41
the flash died after a month 100000 cycles per sector; a log in a single sector sector rotation 41
floating WP#/HOLD# they pick up noise pull them up to the supply 41
settings were lost after power-off in the middle of saving the page was erased, but not written in time a log over two pages 11
after a firmware update the settings "shifted" the structure layout changed a version field, defaults on mismatch 11
a settings write aborts with an error halfway the block is larger than a page: the next one was not erased write no more than a page, or a log 11
interrupts are delayed by tens of ms while saving erasing Flash stalls instruction fetch (F1, single bank) save at a quiet moment; an urgent handler in RAM 11
Guru Meditation (Cache disabled but cached memory region accessed) while writing NVS or updating a handler with the ESP_INTR_FLAG_IRAM flag calls a function without IRAM_ATTR or reads a constant without DRAM_ATTR, while the cache is disabled during the write; ordinary handlers simply wait at that time everything such a handler touches goes into IRAM_ATTR and DRAM_ATTR, or remove the ESP_INTR_FLAG_IRAM flag 150
malloc of 200 KB returned NULL with 300 KB free the ESP32 heap is made of banks, the largest contiguous chunk is smaller split the buffer or MALLOC_CAP_* 150
HardFault right after a call through a function pointer an even jump address: CFSR = 0x00020000 (INVSTATE) take the function's address, not a number; decode CFSR, HFSR and the PC in the exception frame 4б

HardFault analysis by registers: the HFSR bit, the CFSR field, the set bit with its mask and what it usually means, with the section number


36.9 Power and sleep

Symptom Cause Fix §
everything is slow after Stop the clock tree was not restored: HSI instead of PLL restore the PLL after waking up 10
sleep current is higher than calculated pins in an undefined state analog mode or a pull 10
sleep current is tens of µA higher a ~40 kΩ pull-up on a pin held to ground externally remove the pull-up or invert the logic 10
the current in Sleep does not drop peripherals are on turn them off before sleep 10, 33
the current in Stop is as in Sleep the debugger left the DBG_STOP bits in DBGMCU->CR; a reset does not clear them clear the bits or power-cycle 10
the chip resets on wake-up while an ammeter is in the supply the 200 µA range is a 1 kΩ shunt, the supply drops below 2 V a jumper in parallel with the meter 10
multimeter readings jump by tens of times it averages over fractions of a second, the bursts get caught every other time measure sleep and the burst separately, compute the average 10
the programmer does not see the chip after preparing for sleep PA13 and PA14 went into analog mode keep SWD in the mask; flash with connect under reset 10
it sleeps, but not for long (≤ 233 ms) the SysTick limit the core at a lower frequency all the time, or your own sleep timer 33
woke up — the time drifted your own sleep function without vTaskStepTick correct it 33
waking up too often eats the benefit leaving Stop/Standby is not free wake up less often 10
feeding the watchdog from an interrupt the interrupt is alive, the program is dead — the watchdog stays quiet feed it from the main loop 12
the reset cause flags lie not cleared RMVF after reading 12
the reset cause is always "button" PINRSTF is set on almost any reset check it last 12
WWDG resets right after start counter 0x40 — one tick before reset start with 0x7F 12
WWDG resets when fed it is fed before the counter drops to the window W feed inside the window 12
the RTC alarm wakes only once the alarm flag is not cleared (ALRAF, on F1 ALRF): no new edge on EXTI 17 clear the flag in the handler 12
the RTC alarm handler is called endlessly the EXTI 17 line flag is not cleared EXTI->PR = EXTI_PR_PR17 12
the F4 alarm is not written the WPR keys were not entered or ALRAWF was not waited for 0xCA, 0x53, then wait for ALRAWF 12
noise in the ADC/DAC ground, supply, reference layout; filter; VDDA 14, 15
an electronic load does not go down to zero the DAC output buffer is enabled BOFF1: the op-amp input is high-impedance 15
the ADC reference "floats" VDDA from USB through a diode a regulator or an external reference 14
ADC readings drift along with the battery the reference is VDDA, but the calculation assumed 3.3 V recalculate via VREFINT 14
internal ADC channels give zero TSVREFE is not enabled CR2 on F1, ADC->CCR on F4 14
an RTC_DATA_ATTR variable was zeroed after esp_restart it survives only deep sleep, on a software reset the bootloader loads it again for resets — RTC_NOINIT_ATTR and zeroing on ESP_RST_POWERON 157
ESP32 starts from app_main after deep sleep leaving deep sleep is a reset keep the state in RTC memory, check esp_sleep_get_wakeup_cause() 157
an ESP32 sensor drains the battery in weeks a long Wi-Fi connection on every wake-up: 2 s of radio cost more than a day of sleep wake up less often, a static IP, the router channel in RTC memory 157
ESP32 reboots as soon as it turns on the transmitter a dip of the 3.3 V rail at the 240 mA peak: too little capacitance at the module, a long cable 10 µF and 100 µF or more at the module, a low-dropout regulator, a short cable 161

36.10 Hardware

Symptom Cause Fix §
DRC: a short near the microcontroller package the trace from the decoupling capacitor runs diagonally across neighboring pins the capacitor perpendicular to the pin row, with its near pin towards VDD 130, 131
the crystal does not start when cold the capacitors do not match the CL of your crystal C1 = C2 = 2 × (CL − Cstray) 130
a power trace heats up the width is not sized for the current, especially on an inner layer width per IPC-2221: 1 A at 10 °C — 0.30 mm outside, 0.78 mm inside 130
DRC fails in the build, although there are no violations --exit-code-violations also counts unrouted connections use the option when the board is fully routed 131
the DipTrace plugin ran, but the board did not change the exchange file was not rewritten: DipTrace only watches for its change and does not read the exit code rewrite the same plugin_exchange.xml by replacing it entirely 132
an object deleted by the DipTrace plugin stayed on the board the node was removed from the XML set Enabled="N" 132
distances in an inch-based exchange file drift in the third digit four digits in inches is a step of 2.54 µm compare with a tolerance of a couple of micrometers 132
configparser: "File contains no section headers" on an Altium project .PrjPcb starts with a UTF-8 byte order mark read it with the utf-8-sig encoding 133
a KiCad script does not open an Altium board, LoadBoard returned nothing the format is not guessed from the file specify PCB_IO_MGR.ALTIUM_DESIGNER 133
after importing from Altium the net classes are 0.2 mm instead of 0.1 the classes got KiCad settings, not the Altium rules define the net classes again 133
KiCad does not open its own IPC-2581 or ODB++ KiCad only writes these formats transfer as a project or a netlist 134
after importing a netlist the parts have no packages footprint names differ between programs map packages by name, the rest by hand 134
the mechanical engineer got a board without parts no 3D models are attached to the parts attach models before exporting STEP 134
a crystal of the wrong frequency 8 MHz on the board, 25 in the project make them match 6
the system stayed on HSI SW was checked instead of SWS wait for SWS 6
HSE was lost — the device stopped no CSS enable it, handle the NMI 6
UART: lines swapped TX to TX TX → RX 16
Cyrillic text garbled in the terminal the terminal encoding UTF-8 on both sides 16
I²C: wrong address 0x68 versus 0xD0 the 7-bit format with a shift 17
I²C: ADDR is not cleared only SR1 was read SR1, then SR2 17
I²C: works with one module, not with several the modules' pull-ups add up: five of 4.7 kΩ give 940 Ω, more than 3 mA keep the pull-ups on one module only 17, 95
I²C: works on the bench, fails on a cable the cable capacitance stretches the edge: at 400 kHz with 2.2 kΩ — 1.71 m lower speed, recalculate the pull-up, a buffer 17, 95
no common ground the sensor is on its own board connect them 17, 19, 95
an input glitches or heats up with a 5 V module the module pulls the bus up to 5 V remove the module's pull-ups or use a level shifter 62, 95
a thermistor shows −40 or +125 and does not change an open or short in the divider: the code is beyond the table check for out-of-table values, do not report them as temperature 96
SPI: garbage with several devices two CS are active pull-ups on all CS 18
SPI: the display works, the flash does not different modes on one bus switch the mode 18
the SD card is silent on CMD0 a wrong CRC7, too few clocks before the command or a speed above 400 kHz CMD0 is 0x40 00 00 00 00 95, 74 clocks, prescaler 256 18
bytes are swapped on the 74HC595 outputs sent in direct order the byte for the farthest register goes first 18
SWO on F407 prints garbage the debugger has the 8 MHz crystal frequency instead of the 168 MHz core the core frequency in the settings, ACPR = 83 for 2 MHz 21
SWO loses characters and slows the program down byte by byte, a 2 MHz line passes 100000 characters per second write words — 160000, print less 21
an extra key appears pressed on the keypad three keys at the corners of a rectangle without diodes: 144 triples out of 560 a diode on every key 22
the common pin of a display digit heats up eight segments at 8 mA — 64 mA through one pin a transistor as the digit switch 22
CAN without a transceiver direct connection a transceiver is mandatory 19
CAN: terminators not two of 120 at the ends exactly two, at the ends 19
USB: detected as unknown descriptors a protocol analyzer 20
USB: the cable unshielded, thin another cable 20
SPI flash: the mode 0 or 3, not 1 and 2 CPOL=CPHA 41
current leaks through a floating input an input without a pull pull it 8, 10
the relay switch burns out, ESP32 resets when the relay turns off no diode across the coil: the spike breaks down the switch a diode across the coil, cathode to the supply, TVS for remote loads 161
a MOSFET driven from an ESP32 pin heats up, the relay clicks uncertainly the transistor is not logic-level: at 3.3 V it is not fully on a low-threshold transistor or an NPN with a base resistor 161

36.11 Communications and radio

Symptom Cause Fix §
the Wi-Fi module answers ERROR to everything the wrong UART speed or the previous command has not finished speed; wait for the response 37
the module response parser gets confused an unsolicited message arrived between a command and its response parse each line by its prefix 37
the broker disconnects every minute and a half no PINGREQ more often than the keep-alive send it on a timer 37
CONNACK with code 5 not authorized: someone else's or an empty token each device has its own token 37, 91
the connection to the broker silently drops the router removed the NAT entry while there was no traffic keep-alive 86
the server cannot reach the device the device is behind NAT the device connects by itself 86
after the server moved, all devices are silent the firmware has an address, not a name the name in settings that can be changed over the air 87, 91
the server cannot come back up after a failure all devices retry at once a growing delay with random jitter 88
duplicate measurements in the database a POST retry after a dropped connection PUT by number or an idempotency key 88
the request body is cut off on Cyrillic text Content-Length in characters the length in bytes 88
the load balancer sends everything to one server the server group has no shared zone zone in upstream 88
RS-485 loses the last byte the transmitter is turned off on TXE turn it off on TC 89
Modbus: CRC error the CRC bytes are swapped CRC least significant byte first 89
the CoAP server executed a command twice the retry was sent with a new message ID the same ID on a retry 89
Zigbee loses packets near Wi-Fi the channel overlaps with Wi-Fi channels 15, 20, 25, 26 89
a LoRaWAN device goes silent for minutes at SF12 the 1 % duty cycle ran out send less often, a lower SF 89, 93
SX1276: RegVersion reads as 0x00 or 0xFF wires, SPI mode, NSS check in order 93
SX1276 does not transmit, although the registers are written the LoRa bit was not written in sleep mode sleep → LoRa → wait 93
SX1276 is on the wrong frequency Frf was computed in 32 bits compute it in uint64_t 93
LoRa packets do not arrive, no errors different SF, bandwidth or sync word compare the parameters on both sides 93
the network server silently does not see the device the address or key is in the other byte order in the frame — least significant byte first 94
an ABP device went silent after a reboot the frame counter started from zero keep the counter in non-volatile memory 94
commands do not reach a class A device the RX1 window opens too late a timer from the "transmission finished" event 94
the device's silence cannot be interpreted there is no heartbeat message an "I am alive" message at a set interval 90
the coverage map lies the path loss model has no walls walls in the model, a floor plan with materials 92
ESP32: esp_netif is started, but there is no address the event loop is not created or NVS is not initialized nvs_flash_init() and esp_event_loop_create_default() before the network driver 156
ESP32 cannot connect to Mosquitto 2 without listener 1883 and allow_anonymous the broker only admits localhost configure a listener and client credentials 156
the ESP32 log has dates from 1970 SNTP has not synchronized yet, there is no battery-backed clock wait with esp_netif_sntp_sync_wait with a timeout, keep logs in UTC 156
ESP32 is not visible in a BLE scanner after the phone disconnects advertising is not restarted on the disconnect event restart advertising in the BLE_GAP_EVENT_DISCONNECT handler 158
only the first 20 bytes arrive over BLE the default MTU is 23, the header is 3 bytes an MTU exchange or sending in chunks 158

36.12 If nothing fits

Rows that are not in the table are new pitfalls, and they also teach something. The order:

  1. Reduce to the minimum — a ten-line project.
  2. Read the registers — all that belong to the block, in the debugger.
  3. Compare with a known working one — an example from the pack, someone else's board, another chip.
  4. Oscilloscope on the line — what is on the wire, not what should be there.
  5. Write it down — symptom, cause, fix. A year from now it will save a day.

36.13 Summary

Next: section 36а "Instruments on the bench" — the multimeter, the oscilloscope and the logic analyzer: how to look at what the debugger cannot see.

Open in the interactive handbook