Arduino state change with delay

This has me stumped. I'm basing my code on the state change sketch, but using angle threshold from accelerometer.

My state change code:

if (tailState != lastTailState) {
      if (tailState == 1) {
        Serial.println("Tail up");
        buf[bufCount++] = millisCount;
        if (bufCount >= 5) {
          bufCount = 0;
        }
        for (int count = 0; count < 5; count++) {
          if (buf[count] < (millisCount - 3600000)) {
            buf[count] = 0;
          }
          Serial.println(buf[count]);
        }
        TailUpCounter = 0;
        for (int count = 0; count < 5; count++) {
          if (buf[count] > 0) {
            TailUpCounter++;
            Serial.println(TailUpCounter);
          }
        }
      }
      Narcoleptic.delay(100);
      delay(50);
    }
    lastTailState = tailState;

What I want to happen is to check for a change to 1, but continue checking its at 1 for three seconds before doing anything. Basically the angle has to be above 60 for three seconds before the for loops are executed. Hope that makes sense.

I've tried:

const unsigned long duration = 15 * 1000UL;
    static unsigned long lastSampleTime = 0 - averageTimer; 

    unsigned long now = (millis() + Narcoleptic.millis());
    if (now - lastSampleTime >= duration)
    {
      lastSampleTime += duration;
    }

But it seems to work in the opposite manor. On state change it runs the for loops, but then wont register another state change for 3 seconds. Not what I want.

Hope I explained that rite.

Any suggestions?

You want to determine when the state changes to 1. Record the single time that that happens.

Periodically, you want to see if the state IS 1. If so, and now (the state is 1) minus then (the state became 1) is greater than X milliseconds, do the magic deed.

You will be reading the current state of the switch/analog pin on every pass through loop(), but using the state twice - once to see if it changed to 1, and once to see if it is 1 - as separate activities in loop().

Hi Paul,

Have I over complicated it, or is this ok?

if (tailState != lastTailState) {
      currentMillis = (millis() + Narcoleptic.millis());
      State = 1;
      lastTailState = tailState;
    }
    
    if (tailState == 1 && State == 1) {
      if ((millis() + Narcoleptic.millis()) - currentMillis >= interval){
        currentMillis = (millis() + Narcoleptic.millis());
        Serial.println("Tail up");
        buf[bufCount++] = millisCount;
        if (bufCount >= 5) {
          bufCount = 0;
        }
        for (int count = 0; count < 5; count++) {
          if (buf[count] < (millisCount - 3600000)) {
            buf[count] = 0;
          }
          Serial.println(buf[count]);
        }
        TailUpCounter = 0;
        for (int count = 0; count < 5; count++) {
          if (buf[count] > 0) {
            TailUpCounter++;
            Serial.println(TailUpCounter);
          }
        }
      State = 0;
      }
    }

Its working, but is there a simpler way?

Cheers.

but is there a simpler way?

It's hard to say whether or not there is a simpler way, since you have not posted complete code, or made any efforts to make the code readable. I hate { on lines with statements. I prefer the } to line up with the matching {.

I don't see the need to print TailUpCounter every time it changes. You seem to only care about the final value in TailUpCounter.

Some comments in the code to explain what it is doing would definitely be in order.

Sorry full code..

#include <EEPROMex.h>
#include "Arduino.h"
#include <Wire.h> // Must include Wire library for I2C
#include <SFE_MMA8452Q.h>
#include <GPRS_Shield_Arduino.h>
#include <SoftwareSerial.h>
#include <Narcoleptic.h>

#define PIN_TX    10
#define PIN_RX    8
#define BAUDRATE  4800
#define PHONE_NUMBER "+447732869235"
char STORED_PHONE_NUMBER[] = "             ";
#define MESSAGE  "Cow Calving"

#define MESSAGE_LENGTH 160
char message[MESSAGE_LENGTH];
int messageIndex = 0;
int addressCharArray;

int tailState = 0;
int lastTailState = 0;
int TailUpCounter = 0;
int flag = 0;

long buf[5];
int bufCount = 0;

char phone[16];
char datetime[24];

int analogInput = A0;
float vout = 0.0;
float vin = 0.0;
float R1 = 199000.0; //  
float R2 = 465000.0; // 
int batteryPercentage = 0;
char batteryPercentageBuf[5];
int value = 0;

unsigned long currentMillis = 0;
const long interval = 3000UL;  
int State = 0;

GPRS gprs(PIN_TX, PIN_RX, BAUDRATE); //RX,TX,BaudRate

MMA8452Q accel;

void setup()
{
  pinMode(9, OUTPUT);
  digitalWrite(9, LOW);
  pinMode(analogInput, INPUT);
  Serial.begin(9600);
  Narcoleptic.disableTimer1();
  Narcoleptic.disableTimer2();
  //Narcoleptic.disableSerial();
  //Narcoleptic.disableADC();
  //Narcoleptic.disableWire();
  Narcoleptic.disableSPI();
  //while (!Serial){
  //}
  Serial.println("initializing");
  while (!gprs.init()) {
    delay(1000);
    Serial.print("gprs error\r\n");
  }

  while (!accel.init()) {
    delay(1000);
    Serial.print("accel error\r\n");
  }

  EEPROM.readBlock<char>(addressCharArray, STORED_PHONE_NUMBER, 13);

  delay(1000);

  Serial.println("test 1");

  int SETUP_COUNT = 0;
  while (SETUP_COUNT <= 60) {
    messageIndex = gprs.isSMSunread();
    //Serial.println("unread message");
    if (messageIndex > 0) { //At least, there is one UNREAD SMS
      //Serial.println("see's message");
      gprs.readSMS(messageIndex, message, MESSAGE_LENGTH, phone, datetime);
      //Serial.println("read message");
      //In order not to full SIM Memory, is better to delete it
      gprs.deleteSMS(messageIndex);
      //Serial.println("deleted message");
      delay(100);
      //Serial.println("message received");
      Serial.println(message);
      if (strcmp(message, "1234") == 0) {
        EEPROM.writeBlock<char>(addressCharArray, phone, 13);
        EEPROM.readBlock<char>(addressCharArray, STORED_PHONE_NUMBER, 13);
        Serial.println(STORED_PHONE_NUMBER);
        delay(100);
        gprs.sendSMS(STORED_PHONE_NUMBER, "New number set");
        delay(2000);
      }
    }
    SETUP_COUNT++;
    Serial.println("Loop");
    delay(400);
  }
  Serial.println("test 2");

  //Get battery %
  value = analogRead(analogInput);
  //Serial.println(analogInput);
  vout = (value * 3.28) / 1024.0;
  vin = vout / (R2/(R1+R2)); 
  batteryPercentage = ((vin-3.7)*100)/0.5;
  //Serial.println(vin);
  //Serial.println(batteryPercentage);
  itoa(batteryPercentage, batteryPercentageBuf, 10);
  //Serial.println(batteryPercentageBuf);
  strcat(batteryPercentageBuf, "%");
  
  if (STORED_PHONE_NUMBER == "             ") {
    gprs.sendSMS(PHONE_NUMBER, strcat("Device setup, entering sleep mode.  Battery: ", batteryPercentageBuf)); //define phone number and text
    delay(2000);
  }
  else {
    gprs.sendSMS(STORED_PHONE_NUMBER, strcat("Device setup, entering sleep mode.  Battery: ", batteryPercentageBuf)); //define phone number and text
    delay(2000);
  }
  digitalWrite(9, HIGH);
}

void loop()
{
  unsigned long millisCount = (millis() + Narcoleptic.millis()) + 3600000;
  //Serial.println(millis() + Narcoleptic.millis());
  //delay(20);

  //Get battery %
  value = analogRead(analogInput);
  vout = (value * 3.28) / 1024.0;
  vin = vout / (R2/(R1+R2)); 
  batteryPercentage = ((vin-3.7)*100)/0.5;
  itoa(batteryPercentage, batteryPercentageBuf, 10);
  strcat(batteryPercentageBuf, "%");
  
  if (accel.available())
  {
    // First, use accel.read() to read the new variables:
    accel.read();

    // apply trigonometry to get the pitch and roll:
    //float pitch = atan(accel.cx / sqrt(pow(accel.cy, 2) + pow(accel.cz, 2)));
    float roll = atan(accel.cz / sqrt(pow(accel.cx, 2) + pow(accel.cy, 2)));
    //convert radians into degrees
    //pitch = pitch * (180.0 / PI);
    roll = roll * (180.0 / PI) ;
    //Serial.println(pitch);
    Serial.println(roll);
    delay(10);

    //Angle of tail
    if (roll > 70) {
      tailState = 1;
    }
    else{
      tailState = 0;
    }
    delay(10);

    if (tailState != lastTailState) { //Tail up or down
      currentMillis = (millis() + Narcoleptic.millis());
      State = 1; //If state has changed
      lastTailState = tailState;
    }
    
    if (tailState == 1 && State == 1) { //If tail is up and last state change was up
      if ((millis() + Narcoleptic.millis()) - currentMillis >= interval){
        currentMillis = (millis() + Narcoleptic.millis());
        Serial.println("Tail up");
        buf[bufCount++] = millisCount; //Add millis to array
        if (bufCount >= 5) { //If buf is more than 5 reset to zero
          bufCount = 0;
        }
        for (int count = 0; count < 5; count++) { //Check if millis times in buf are older than 1 hour, and reset to zero if they are
          if (buf[count] < (millisCount - 3600000)) {
            buf[count] = 0;
          }
          Serial.println(buf[count]);
        }
        TailUpCounter = 0;
        for (int count = 0; count < 5; count++) { //Count how many times tail was up by counting if buf is greater than zero
          if (buf[count] > 0) {
            TailUpCounter++;
            Serial.println(TailUpCounter);
          }
        }
      State = 0;
      }
    }
    delay(50);
    
    if (TailUpCounter == 5) {
      digitalWrite(9, LOW);

      int INI_COUNT = 0;
      while (INI_COUNT <= 15) { //Delay for gsm connection
        Narcoleptic.delay(4000);
        INI_COUNT++;
      }

      //Send message
      Serial.println("Sending message");
      delay(100);
      if (STORED_PHONE_NUMBER == "             ") {
        gprs.sendSMS(PHONE_NUMBER, strcat("Cow Calving.  Battery: ", batteryPercentageBuf)); //define phone number and text
        delay(2000);

        digitalWrite(9, HIGH);
        delay(1000);
        while(1){  
          Narcoleptic.delay(8000);
        }
      }
      else {
        gprs.sendSMS(STORED_PHONE_NUMBER, strcat("Cow Calving.  Battery: ", batteryPercentageBuf)); //define phone number and text
        delay(2000);

        digitalWrite(9, HIGH);
        delay(1000);
        while(1){
          Narcoleptic.delay(8000);
        }
      }
    }
    
    if(batteryPercentage < 20 && flag == 0){
      digitalWrite(9, LOW);
      int INI_COUNT2 = 0;
      while (INI_COUNT2 <= 15) {
        Narcoleptic.delay(4000);
        INI_COUNT2++;
      }
      if (STORED_PHONE_NUMBER == "             ") {
        gprs.sendSMS(PHONE_NUMBER, strcat(batteryPercentageBuf," Battery.  Recharge now")); //define phone number and text
        delay(2000);

        digitalWrite(9, HIGH);
        delay(1000);
      }
      else {
        gprs.sendSMS(STORED_PHONE_NUMBER, strcat(batteryPercentageBuf," Battery.  Recharge now")); //define phone number and text
        delay(2000);

        digitalWrite(9, HIGH);
        delay(1000);
      }
    flag = 1;
    }
    
    delay(100);
    Narcoleptic.delay(500);
  }
}

Sorry about curly brackets, but that's how I'm used to doing it.

Damien.

int addressCharArray;

The WTF light exploded.

Is that supposed to be addressOfPhoneNumber?

    gprs.sendSMS(PHONE_NUMBER, strcat("Device setup, entering sleep mode.  Battery: ", batteryPercentageBuf)); //define phone number and text

You can NOT concatenate an array to the end of a string literal. You do NOT own the memory where the literal is stored. Even if you did, there is no extra space to put more data.

In any case, I think that determining that a single state change happened, and that the state has remained the same can be done in a much simpler way. Something like this, that has a lot of details left out or replaced by comments.

You are detecting the state change now. Record when that event happens:

unsigned long tailWentUp = 0;

void loop()
{
   // Read the device that indicates the tail position
   if(tailState != lastTailState)
   {
      if(tailState == 1)
         tailWentUp = millis(); // Or something more complicated involving Narcoleptic.millis()
   }

   // If the tail went up, and is still up
   unsigned long now = millis(); // Or something more complicated...
   if(tailWentUp > 0 && now - tailWentUp > ohNoShesHavingACalf)
   {
      OhNoShesHavingACalf();
      tailWentUp = 0;
   }
}

Much appreciated as always :slight_smile:

PaulS:
The WTF light exploded.

Is that supposed to be addressOfPhoneNumber?

Got that from the eepromex library, and just never changed it :slight_smile:

PaulS:

    gprs.sendSMS(PHONE_NUMBER, strcat("Device setup, entering sleep mode.  Battery: ", batteryPercentageBuf)); //define phone number and text

It was the only way I could get it to send the message along with the battery percentage. It seems to work

I don't like using Narceloptic but I couldn't see a way of using millis with the low power library. Can this be done?

Cheers.

It seems to work

You mean that it hasn't bitten you in the ass yet. It will, if you don't fix it.

I don't know how, I'm just hoping not to get bitten :slight_smile:

What about millis and the low power library?

Cheers

Do you mean add them outside the send message function?

Do you mean add them outside the send message function?

Add what? The variable used to hold the time that the state change happens needs to be global. The rest of the code replaces the convoluted logic you have of counting times.

PaulS:

    gprs.sendSMS(PHONE_NUMBER, strcat("Device setup, entering sleep mode.  Battery: ", batteryPercentageBuf)); //define phone number and text

You can NOT concatenate an array to the end of a string literal. You do NOT own the memory where the literal is stored. Even if you did, there is no extra space to put more data.

Sorry I meant this. Can I add the array to string outside the send message function?

Can I add the array to string outside the send message function?

What array? To what string? Where?

You can manipulate any array in any way anywhere that it is in scope.