Millis, SMSs y SIMs

Hola
Llevo mucho tiempo buscando algo que me convenza y de momento no estoy del todo satisfecho, aunque el sistema funciona.
Como sabréis, las compañías de teléfonos dan un margen de entre 3 y 6 meses para realizar consumos con la SIM. En caso contrario desactivan la SIM o incluso la dan de baja directamente.
Para evitar esto, implementé un sistema para que, pasados 49 días (una vuelta de millis), el UNO emitiera un SMS vía SIM900 para re-activar la SIM.

A groso modo el sistema es:

#define MARGEN 4000 // equivale a lo que en teoría puede tardar de ejecutarse un loop como máximo
unsigned long ult_SMS;

setup()
{
 ult_SMS = millis()-(MARGEN+100); // reinicio del UNO. esperar 49 días
}

send_sms()
{
.
.
  SIM900.println((char)26); //Comando AT de finalización: ^Z (ASCII 26)

  ult_SMS = millis()-(MARGEN+100); //ultimo SMS enviado. descontar MARGEN+100 para no igualar a millis();
  if(ult_SMS==0) ult_SMS--; // evitar que sea 0
}

loop()
{
  unsigned long tiempo=millis();
 // le damos un margen de (MARGEN) +-4000 (MARGEN * 2 = 8 segundos)
  if (tiempo >= ult_SMS - MARGEN && tiempo <= ult_SMS + MARGEN) send_sms();
.
.
.
}

Para el margen he contado todo lo que hace o pueda hacer el UNO aunque sea imposible que lo haga todo en un loop

¿Hay alguna forma mejor de hacer esto?

Saludos y gracias

PD: veo que esta duplicado. Cuando lo tenia terminado, se me ha bloqueado el navegador y creía que lo había perdido. Por favor borren el otro

Te he hecho una pequeña clase para que puedas controlar que haya transcurrido la cantidad arbitraria de días que quieras. Puedes usarla para controlar otras unidades de tiempo que no sean días.

const unsigned long UN_SEGUNDO = 1000UL;
const unsigned long UN_MINUTO = 60000UL;
const unsigned long UNA_HORA = 3600000UL;
const unsigned long UN_DIA =  86400000UL;

// Clase para controlar la cantidad de "unidades" de tiempo transcurrida
class Transcurrido {
    public:
        // Constructor de la clase, indicar la unidad de tiempo en milisegundos
        Transcurrido(unsigned long unidad) : cantidad(0), unidad(unidad), t(millis()) {}
        // Pone el conteo a cero
        void clear() {this->cantidad = 0;}
        // Obtiene el valor del conteo
        uint32_t getCantidad() {
            unsigned long ahora = millis();
            while ((ahora - this->t) >= this->unidad) {
                this->cantidad++;
                this->t += this->unidad;
            }
            return this->cantidad;
        }
    private:
        uint32_t cantidad;
        unsigned long unidad;
        unsigned long t;
};

Transcurrido diasTranscurridos(UN_DIA); // Lo definimos para que nos cuente días (UN_DIA = 86400000UL milisegundos)

void setup() {
    Serial.begin(9600);
}

void loop() {
    if (diasTranscurridos.getCantidad() >= 10) { // Si ha transcurrido 10 días o más
        Serial.println(F("Han transcurrido 10 días"));
        diasTranscurridos.clear();  // Ponemos a cero el contador
    }
}

He puesto un ejemplo de 10 días, pero si en Transcurrido diasTranscurridos(UN_DIA); se cambia UN_DIA por UN_SEGUNDO, se podrá probar que el mensaje sale cada diez segundos.

No olvidarse de llamar a diasTranscurridos.clear() para poner el contador a cero. Se puede llamar a diasTranscurridos.clear() en cualquier momento que se quiera volver a empezar la cuenta. Esto se puede aprovechar para poner a cero la cantidad de días cuando se manda un SMS por cualquier motivo, antes de que se cumplan los días que quieres. Así se mandaría el SMS sólo si han transcurrido X días sin mandar ningun SMS de cualquier tipo. Ojo, si el Arduino se apaga o se resetea, la cuenta se pone a cero.

Si se emplease para controlar segundos, hay que tener en cuenta que si se llama a .getCantidad() en intervalos mayores a un segundo, las cantidades que retorna podrían no ser consecutivas. Por eso la comparación ha de ser >=.

Muchas gracias IgnoranteAbsoluto.

Lo he estado repasando y sigo con la misma duda, aunque parece que me expliqué mal. Si las compañías dan entre 3 y 6 meses para hacer un consumo, ¿como puedo hacer que supere los 49 días? Pero sobre todo quiero mejorar mi código para que no falle y, a ser posible, mucho mas simple. Por lo que veo, tu código funciona perfecto, pero no supera los 49 días del millis(). ¿O me equivoco?

De nuevo muchas gracias por tu aporte y un saludo.

El contador que uso es un entero de 32 bits sin signo. Así que en límite es 65535. Por lo que si cuentas días, el límte es de 179 años. En el ejemplo he puesto 10 días, tú pueds poner 180 si quieres.

ufff. no lo he visto bien. Gracias

IgnoranteAbsoluto:
El contador que uso es un entero de 32 bits sin signo. Así que en límite es 65535.

Me quedó haciendo ruido esta afirmación.
Ignorante dices usar 32 bits sin signo o sea 31 bits = 2^31 = ‭2,147,483,648‬ y no 65535 que sería 2^16.

Estoy mal o se ha deslizado un error de interpretación de mi parte?

@surbyte, tienes razón, me equivoqué. En todo momento estaba pensando que con un simple entero sin signo de 16 bits había más que suficiente para contar los días que se pretendían contar. Pues con el entero sin signo de 16 bits serían los 65535 díás en que estaba pensando y, sin duda alguna, daría para contar varios años. Pero es cierto, no eran los 16 bits que se me habían "metido en la cabeza", eran 32 bits sin signo, y con eso da para contar 4294967295 días (más de once millones de años). Lo que dice @surbyte es la cantidad de días si el entero de 32 bit fuera con signo. Pero en este caso es sin signo, con lo que disponemos del doble.