Tenes distinto acercamientos, el mas comun es el siguiente.
Primero podrias usar sprintf para imprimir sobre un array, y luego enviar por ese array
Tu printSerial deberia enviar un string, ya que un float tiene varios numeros, no uno solo.
float var1 = 1.234;
char txt[10];
printSerial(txt); // Imprime varios char
Como vos decis no existe forma de recrear un "overload" de funciones como posee C++.
Podrias usar un puntero a void, pero necesitarias si o si identificar el tipo de argumento que pasas:
void funcion(void * data, enum tipo_t tipo)
{
switch(tipo)
{
case INT:
funcion_int((int *)data);
break;
case FLOAT:
funcion_float((int *)data);
break;
}
}
De esa forma segun el tipo podrias hacer una cosa u otra. Aun asi, tanto en C como C++ tenes que escribir cada una de las funciones. Pero ojo que podes pasar un char y ponerle tipo FLOAT, teniendo todos los posibles problemas que pueden existir.
La otra opcion que vi es crear tu propio printf, y a medida que se va enviando el texto que uno le pasa, va llamando a cada una de las variables... pero los float es un dolor de cabeza computacional para el micro. Tambien es lidiar con funciones con numeros de argumentos variables. Y como decia a medida que se van enviando las letras al detectar un % se fija la proxima letra para saber como tomar el valor que uno le pasa (si float,int, etc)
La otra es usar una libreria:
https://github.com/mpaland/printf