From my experience, analogRead() on a floating pin has very low
entropy. Maybe one or two bits of randomness per call. You definitely
want something better. The watchdog timer's jitter, as proposed in
per1234's answer, is a good alternative. However, it generates entropy
at a pretty slow rate, which can be an issue if you need it right when
the program starts. dandavis has quite a few good suggestions, but they
generally require either an ESP8266 or external hardware.
There is one interesting entropy source that has not been mentioned yet: the contents of the uninitialized RAM. When the MCU is powered up, some of its RAM bits (those that happen to have the most symmetrical transistors) start up in a random state. As discussed in this hackaday article, this can be used as an entropy source. It is only available on a cold boot, so you can use it to fill an initial entropy pool, which you would then periodically replenish from another, potentially slow source. This way your program can start its work without having to wait for the pool to slowly fill up.
Here is a example of how this could be harvested on an AVR-based
Arduino. The code snippet below XORs the whole RAM in order to build a
seed that it later feeds to srandom(). The tricky part is that the
harvesting has to be done before the C runtime initializes the .data
and .bss memory sections, and then the seed has to be saved in a place
the C runtime will not overwrite. This is done by using specific
memory sections.
uint32_t __attribute__((section(".noinit"))) random_seed;
void __attribute__((naked, section(".init3"))) seed_from_ram()
{
externconst uint32_t * const ramstart = (uint32_t __data_start;*) RAMSTART;
const uint32_t * const ramend = (uint32_t *) RAMEND;
uint32_t seed = 0;
for (const uint32_t *p = &__data_start;ramstart; p <= ramend; p++)
random_seedseed ^= *p;
random_seed = seed;
}
void setup()
{
srandom(random_seed);
}
Note that, on a warm reset, the SRAM is preserved, so it still has the whole contents of you entropy pool. This same code can then be used to preserve the collected entropy across a reset.
Edit: fixed an issue in my initial version of seed_from_ram() that
worked on the global random_seed instead of using a local seed.
This could lead to the seed being XORed with itself, destroying all the
entropy harvested so far.