If you run an S3 or C3 board plugged into a computer, and you do not have a serial
monitor open, you are losing about 19% of your hash rate. One line fixes it.
Measurements
Same miner (T-Display-S3), before and after, read from the pool-side dashboard so
that nothing was attached to the serial port during the measurement:
before 255 kH/s 58.8 C
after 304 kH/s 62.8 C
The temperature is the giveaway: the die runs cooler while the miner is broken,
because it is genuinely doing less work.
Watching it degrade, again with no serial reader attached, one sample per 75 s:
280.7 kH/s 62.8 C
276.9 kH/s 62.8 C
255.0 kH/s 61.8 C
255.0 kH/s 60.8 C
256.2 kH/s 60.8 C
255.3 kH/s 60.8 C
It settles around 255. The five sibling miners on the same firmware, powered from a
plain USB supply rather than a host, held 300.5 throughout. And opening a serial
reader on the affected board brings it straight back to 304.
Cause
HWCDC.cpp in the Arduino core starts with:
static uint32_t tx_timeout_ms = 0;
and raises it as soon as a USB host enumerates the port:
HWCDC::write() then waits up to that timeout on the TX ring buffer:
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return 0;
}
...
if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size,
tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
A host that enumerates the port but never reads it lets that buffer fill up and stay
full. Every log line then costs the calling task up to 100 ms.
The reason this hits hash rate rather than just delaying logs is which task does the
logging. runStratumWorker prints on every job (Sending, Receiving, target,
coinbase, and so on) and is also the task that calls JobPush() to refill the
miners' queues. While it is blocked on Serial, it is not handing out work, so the
mining tasks drain their queue and idle.
That also explains why both paths drop together and in proportion: they are not slower,
they are being starved.
Why this is a common configuration
USB is how most of these boards are powered. Plugging one into a spare port on a
desktop or a laptop is an obvious thing to do, and there is nothing on screen to
suggest anything is wrong: the display shows a hash rate, the pool shows the worker,
and the number is simply lower than it should be. Users comparing boards, or comparing
against a closed-source firmware, would see a difference with no visible cause.
A plain USB charger does not enumerate a CDC endpoint, so it does not trigger this.
It needs a host.
Fix
#if ARDUINO_USB_CDC_ON_BOOT
Serial.setTxTimeoutMs(0);
#endif
Writes become non-blocking: log lines are dropped when the buffer is full instead of
stalling the task that emitted them. Nothing else changes, and with a monitor attached
the logs are unaffected.
https://github.com/Gheop/NerdMiner_v2/tree/fix/usb-cdc-blocks-mining
Guarded by ARDUINO_USB_CDC_ON_BOOT, so boards using a UART bridge (CP2102, CH340)
compile and behave exactly as before. Builds verified for NerdminerV2 and
TTGO-T-Display.
If you run an S3 or C3 board plugged into a computer, and you do not have a serial
monitor open, you are losing about 19% of your hash rate. One line fixes it.
Measurements
Same miner (T-Display-S3), before and after, read from the pool-side dashboard so
that nothing was attached to the serial port during the measurement:
The temperature is the giveaway: the die runs cooler while the miner is broken,
because it is genuinely doing less work.
Watching it degrade, again with no serial reader attached, one sample per 75 s:
It settles around 255. The five sibling miners on the same firmware, powered from a
plain USB supply rather than a host, held 300.5 throughout. And opening a serial
reader on the affected board brings it straight back to 304.
Cause
HWCDC.cppin the Arduino core starts with:and raises it as soon as a USB host enumerates the port:
HWCDC::write()then waits up to that timeout on the TX ring buffer:A host that enumerates the port but never reads it lets that buffer fill up and stay
full. Every log line then costs the calling task up to 100 ms.
The reason this hits hash rate rather than just delaying logs is which task does the
logging.
runStratumWorkerprints on every job (Sending,Receiving,target,coinbase, and so on) and is also the task that callsJobPush()to refill theminers' queues. While it is blocked on
Serial, it is not handing out work, so themining tasks drain their queue and idle.
That also explains why both paths drop together and in proportion: they are not slower,
they are being starved.
Why this is a common configuration
USB is how most of these boards are powered. Plugging one into a spare port on a
desktop or a laptop is an obvious thing to do, and there is nothing on screen to
suggest anything is wrong: the display shows a hash rate, the pool shows the worker,
and the number is simply lower than it should be. Users comparing boards, or comparing
against a closed-source firmware, would see a difference with no visible cause.
A plain USB charger does not enumerate a CDC endpoint, so it does not trigger this.
It needs a host.
Fix
Writes become non-blocking: log lines are dropped when the buffer is full instead of
stalling the task that emitted them. Nothing else changes, and with a monitor attached
the logs are unaffected.
https://github.com/Gheop/NerdMiner_v2/tree/fix/usb-cdc-blocks-mining
Guarded by
ARDUINO_USB_CDC_ON_BOOT, so boards using a UART bridge (CP2102, CH340)compile and behave exactly as before. Builds verified for
NerdminerV2andTTGO-T-Display.