Count car-starts



  • Latest version 1.13
    I want to store how many times I start my car each day, i will not use it to anything just for fun.
    I was thinking about using S_RAIN since that value will be reseted every night, which I want. One value for each day. I dont care that it will be showing mm and so on, I will just look at the number.
    I am using Domoticz and I can get it to work as long as I am at home.
    I start my Arduino, it receives VAR1 from Domoticz, Arduino add 1 to the received value and sent it back.
    My problem now is that I want to store how many times my car has been started away from my house where I dont have any connection to my gateway/domoticz.
    scenario: I start my car at home it will add 1 to the received number, I drive away to work, turn of my car. A few hours later I start my car, it will NOT receive VAR1 from Domoticz, so it has to store 1 to EPROM. When I get back home it needs to receive VAR1 and add 1 and send it back again.
    Is it possible to get an indication that Arduino didnt receive VAR1 from Domoticz, if yes, then I can write a row that it will store 1 to EPROM and if i start my car many times away from home it just read from EPROM and add 1 to EPROM.
    I can set it up that it try to send data to Domoticz every minute so as soon as I come home it will send data and then go to sleep forever. If the number will be send the day after it is not a problem this is just for fun.
    Sorry folks if this is difficult to read, i will try to write it better tomorrow i have tried to do this for hours without any solution, now I need to sleep.
    Thanks for all help
    good night


  • Mod

    Cool ida, @flopp

    What you are describing should work. You might want to consider what happens if your car is away during the night, or when you go for a week-long road trip. The sensor is not aware of time unless you connect a rtc so you might get 50 starts reported on the day you arrive home. That might not happen very often though, so it might be easier to ignore that aspect of the problem.



  • @mfalkvidd
    Thanks.
    I also had that in my mind and to start with I will see how many times that may happen and if needed I will add an RTC but then I have 2 options

    1. On midnight set eprom to 0(zero)
    2. Add time and date to the variable that I send to Domoticz(but it can't handle date and time) this means maybe I have to change to other Controller;(

    So it must then be option 1.


  • Hardware Contributor

    @flopp As long as you have it powered you dont need to save it into eeprom (its offcourse safer) but you can just use a int variable?
    You could use a battery sensor or how do you plan to detect the start of the car?



  • @sundberg84

    I will not use battery.
    As soon as the arduino start it will just send 1 and each time it starts and cannot send to Domoticz it will add 1 to eprom.

    Is it possible to get some kind of indication that it fails to send the data to Domoticz?


  • Hardware Contributor

    You have st:fail if you dont get ack back from your gateway or repeater.
    gw.send() returns false when no ack was received.

    This can mean though that you can reach the destination, but the ack fails... so its not 100% if you are just on the range limit.

    I saw some threads about code where you could use this to determine (and resend or in your case save to eeprom) actions.

    http://forum.mysensors.org/topic/1424/resend-if-st-fail/5
    http://forum.mysensors.org/topic/2189/serial-api-noack-when-sending-with-ack-failed/2

    (Ok, i hijacked those links from @mfalkvidd in another thread hehe).



  • @sundberg84
    Thanks that could be something to implement.

    Will look to it later.

    Maybe a start would be to write to eprom every time even if it succefully send the data and later on take care of st:fail.

    I know that eprom only can handle 100.000 writes but arduino pro mini is cheap so I can change the hardware when it fails to write to eprom


  • Hardware Contributor

    10start / day? * 300 days/year? = 3000/year
    10000/30000 = 3 years life... if you use those cheap pro mini clones im note sure the eeprom will fail first.



  • @sundberg84
    Hehe even more I think

    100000/3000=33 years

    I think in both cases eprom is fine but arduino will damage first

    I will start testing a code in a few hours
    I update here later



  • This post is deleted!


  • I have had some small problems with my sketch.

    I have 2 cars, A and B.

    A, as soon as I put in the key without turning it, USB port gets power. When I start the car, USB port losses power for a short while and Arduino restarts. This means that I will send data twice. Possible solution for this is to delay for 1-4 minutes but then I will not send anything when I am at home.

    B, when I unlock the car from remote control it power USB for ~30 seconds. This will result in same problem as car A.

    two things I want to implement to sketch.

    1. Send data as soon as I come home, so I can track exact date when I was using car. Not very important.
    2. Only send data when I actually use the car, not when I power Arduino ON. To solve this I have two solutins, GPS or Gyro. Gyro is the cheap and easy part so I will go for that at the moment. GPS should be perfect, then I can control that it should send data when I start to move and also send as soon as I come home.

  • Mod

    Solution 1 should be easy to implement by replacing your endless while loops with sending until you get an ack from the controller.

    An alternative solution could be to fetch the current time from the controller at startup, and compare the current time to the last time a report was sent. If the time was less than 5 minutes ago, don't do anything. If the time is more than 5 minutes ago, store current time in the eeprom, increment the number of starts and send the new value to the controller.



  • @mfalkvidd
    I used While just to not try to send data all the time, since it will only be able to send when I leave my house and not away from my house and my thought was not to use MCU so much maybe this is not a problem. I will test to not sleep until it have sent the data successful in this way it will send data within a few seconds as soon as I reach my house.

    Perfect idea about fetch time, yes this is definitely a solution for my USB-power-on problem

    Thanks


  • Admin

    A solution could also be to hack into the wire harness of the car, and detect when the starter motor gets powered up by turning the key. It's 12v so a simple voltage divider could do it. (and perhaps a couple of diodes from arduino pin to GND and vcc for extra protection.)



  • @tbowmo
    Yes, but I dont want to jeopardize my cars 🙂
    12v can be handle by the Arduino Pro min on RAW pin



  • @mfalkvidd said:
    ... store current time in the eeprom...

    How can I store timestamp to EEPROM, it can only handle a value maximum 256 or?


  • Mod

    @flopp correct. The value needs to be split into four eeprom "slots"

    See http://playground.arduino.cc/Code/EEPROMReadWriteLong for info on how to do that.



  • @mfalkvidd
    thanks



  • New version 1.1

    // Made by Daniel Nilsson
    // Tested with Domoticz 2.4440
    // 2016-03-12
    
    #include <SPI.h>
    #include <MySensor.h>
    
    #define CHILD_ID 0                          // Id of the sensor child
    #define NODE_ID AUTO                        // a number or AUTO to let controller assign
    #define SKETCH_NAME "Car start counter"     // Change to a fancy name you like
    #define SKETCH_VERSION "1.1"                // Your version
    
    int Controller;                             // Current start counts from Controller, like Domoticz
    boolean pcReceived = false;                 // If we have recieved the start counts from Controller or not 
    int starts;                                 // summary of all starts to be sent to Controller
    int eeprom;                                 // start counts read from/to be stored in EEPROM
    
    MySensor gw;
    MyMessage volumeMsg(CHILD_ID,V_RAIN);
    MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
    
    void setup()
    {          
      delay(500);   // wait for radio
      delay(2*60000);  // Allow time if USB/cigarett plug is powered before you turned the key
    
      //Begin
      gw.begin(incomingMessage, NODE_ID, false);
      
      // Send the Sketch Version Information to the Gateway
      gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION);
    
      // Register this device as Rain sensor (will not show in Domoticz until first value arrives)
      gw.present(CHILD_ID, S_RAIN);       
      Serial.println("");
      eeprom = gw.loadState(0);                       // read EEPROM
      Serial.print(eeprom);                           // print EEPROM
      Serial.println(" starts have not been sent");
      Serial.println("add 1 start");
      Serial.print(eeprom);
      Serial.print("+1=");
      eeprom = eeprom + 1;
      Serial.println(eeprom);
      gw.saveState(0,eeprom);                         // store to EEPROM at position 0
      Serial.println("");
      
      Serial.println("Startup completed");
    }
    
    void loop()
    { 
      
    //gw.process();
    
        //See if we have the start counts from Controller - and ask for it if we dont.
        if (!pcReceived) {
          
          Serial.println("Request start counts");
          gw.request(CHILD_ID, V_VAR1);
          //gw.process();
          gw.wait(5000);
          return;
        }
    
    Serial.println("");
    eeprom = gw.loadState(0);                     // read EEPROM
    Serial.print(eeprom);
    Serial.println(" starts have not been sent");
    Serial.print(Controller);
    Serial.println(" starts from Controller = ");    
    starts = Controller + eeprom;                 // total starts
    Serial.print(eeprom);
    Serial.print("+");
    Serial.print(Controller);
    Serial.print("=");
    Serial.println(starts);
    Serial.print("Send ");
    Serial.print(starts);
    Serial.println(" to Controller");
    Serial.println("");
    
    resend((volumeMsg.set(starts)), 5);
    //gw.send(volumeMsg.set(starts));
    resend((lastCounterMsg.set(starts)), 5);
    //gw.send(lastCounterMsg.set(starts));
    gw.wait(1000);
    Serial.println("");
    Serial.println("store 0 to EEPROM");
    gw.saveState(0,0);                            // set 0 start to EEPROM, all have been sent
    Serial.println("sleep");                      // mission accomplished
    while(1){}
    
    }
    
    // check if "st:fail" during gw.send, thanks n3ro
    void resend(MyMessage &msg, int repeats)
    {
      int repeat = 1;
      boolean sendOK = false;
      int repeatdelay = 2000;
    
    
      while ((sendOK == false) and (repeat < repeats)) {
        if (gw.send(msg)) {
          sendOK = true;
        }
        else {
          sendOK = false;
          Serial.print("Error ");
          Serial.println(repeat);
        }
        repeat++; delay(repeatdelay);
      }
      if (sendOK == false && repeat == repeats){
        loop();
      }
    }
    
    //Read if we have a incoming message.
    void incomingMessage(const MyMessage &message) {
      if (message.type==V_VAR1) {
        Controller = message.getULong();
        pcReceived = true;
        Serial.print("Received start counts from Controller: ");
        Serial.println(Controller);   
      }
    }
    

    thanks to @n3ro for st:fail and @sundberg84 for the hint
    @mfalkvidd maybe next version will have timestamp from Domoticz, i am still waiting for an answer if I can send date and time to database. As you can see I have removed while-loop until it successfully sent data to Controller


  • Mod

    Great work @flopp! Just one comment: You call loop if the 5 resend tries isn't enough. That will re-start the loop. What it also will do is to keep all variables in the loop and resend functions in memory. These will add up over time, which will crash your Arduino after a while. So if you start the engine at work and it takes too long time to reach home, the Arduino will have crashed before it is able to update the gateway. See http://arduino.stackexchange.com/questions/355/how-much-can-i-recurse-how-much-can-i-recurse-how-much-caqfsdrfw for technical details on stack usage and recursion.

    You might want to do something like this instead:

    bool resend(MyMessage &msg, int repeats)
    {
    ...
      if (sendOK == false && repeat == repeats){
        return false;
      }
      return true;
    }
    

    and then change all

    resend(..., 5);
    

    to

    if (!resend(..., 5)) return;
    

    This will have the same effect, but will not accumulate stuff on the stack.



  • @mfalkvidd
    Thanks for taking time to read my sketch 😆

    if !(resend((lastCounterMsg.set(starts)), 5)) return;
    

    Arduino IDE says

    Car_counter.ino.ino: In function 'void resend(MyMessage&, int)':
    Car_counter.ino:117: error: return-statement with a value, in function returning 'void' [-fpermissive]
    Car_counter.ino:119: error: return-statement with a value, in function returning 'void' [-fpermissive]
    expected '(' before '!' token
    


  • @mfalkvidd
    this must help, or?

    void send();
    resend((volumeMsg.set(starts)), 5);
    resend((lastCounterMsg.set(starts)), 5);
    gw.wait(1000);
    Serial.println("");
    Serial.println("store 0 to EEPROM");
    gw.saveState(0,0);                            // set 0 start to EEPROM, all have been sent
    Serial.println("sleep");                      // mission accomplished
    while(1){}
    
    }
    
    // check if "st:fail" during gw.send, thanks n3ro
    void resend(MyMessage &msg, int repeats)
    {
      int repeat = 1;
      boolean sendOK = false;
      int repeatdelay = 2000;
    
      while ((sendOK == false) and (repeat < repeats)) {
        if (gw.send(msg)) {
          sendOK = true;
        }
        else {
          sendOK = false;
          Serial.print("Error ");
          Serial.println(repeat);
        }
        repeat++; delay(repeatdelay);
      }
      if (sendOK == false && repeat == repeats){
        void send();;
      }
    

  • Mod

    Sorry, I forgot to change void resend to bool resend. I have updated my previous post.


  • Mod

    And no, movin the code from loop to send does not help. The problem is that you will get recursive calls. send calls resend which calls send which calls resend which calls send which calls resend....and so on. For each call, the Arduino will store all variables on the stack and memory usage will grow and grow.



  • @mfalkvidd
    OK, so you have to go back to where it comes from



  • @mfalkvidd
    Sorry, didnt work. Almost same error

    Car_counter_1.2:84: error: expected '(' before '!' token
    Car_counter_1.2:86: error: expected '(' before '!' token
    expected '(' before '!' token
    


  • @mfalkvidd
    But if I put ! inside ( it works

    if (!resend((volumeMsg.set(starts)), 5))return;
    

  • Mod

    Yes, sorry about that. I edited my previous post to have ! inside ().



  • This post is deleted!

  • Hero Member

    OK, let me see if I understand your overall flow.

    You have your node connected to switched 12V power in the car. When you turn on the car, the uC gets power, when you turn off the car it loses power. When the car is cranking, the voltage drops enough that it tends to reset again (or else the cranking position on your key switch actually cuts off auxiliary power used by the uC).

    So if you are at home and in range, loop() is called by the Arduino runtime just once; at the end of loop() the uC goes into an infinite busy loop rather than return, staying there until power is lost again. If you are away from home or the packets are lost for other reasons, resend() will cause a return from loop(), so that loop() will be called again by the Arduino runtime, so it will just keep trying until either power is lost, or it makes contact with your wireless network again.

    Meanwhile every time it starts, the first byte of eeprom is incremented in setup(), and then loop() tries to fetch VAR1, and send VAR1+eeprom(0) back. If it succeeds, then the first byte of eeprom is zeroed.

    Right?

    That sounds reasonable. My immediate concern would be inconsistent operation during cranking, if the input voltage went just low enough to make the uC unstable. If your car's wiring cuts the power entirely, not a problem. Likewise there appears to be time for the uC to boot, and fully update eeprom, before losing power during cranking. (It's best not to be writing eeprom as voltage drops). It appears that those are not problems for your system tho.


  • Hero Member

    @flopp said:

    12v can be handle by the Arduino Pro min on RAW pin

    Cars go well above 14V during operation (as well as having noise glitches on top of that). If the regulator is only good up to 12v, operation could be marginal. Hence the suggestion from @tbowmo of a couple of diodes in the power feed, to drop a bit of the voltage as seen by the regulator.



  • @Zeph
    Fully correct 🙂 thanks for summarizing.

    Car A have a built in USB where I, today, take power. I have to test if the voltage will go low so the uC will not get correct power but not 0 volt(restart), I don't know what will happen then. This was your question?
    Car B have cigarette plug(12v output) so I use a converter to 5V, I think these converters can handle low input and still have 5V on output.

    I will test it for a while and count starts in my head(good luck)

    Next idea is to have a buzzer to beep when it has successfully sent data to Controller, just as information and if it beeps when I am away from home I have a mistake in the sketch



  • @Zeph
    you are right about 14V.

    Best should be to hide uC and connect to power(which only have power after crank)


  • Mod

    @Zeph said:

    It's best not to be writing eeprom as voltage drops

    This is correct. However, shouldn't the BOD take care of that problem? Might be a good idea to set the BOD to 4.3V i stead of the default 2.7 though?



  • @mfalkvidd said:

    @Zeph said:

    It's best not to be writing eeprom as voltage drops

    What does that mean exactly? Write to eeprom when power disappear, how is that possible without power?

    This is correct. However, shouldn't the BOD take care of that problem? Might be a good idea to set the BOD to 4.3V instead of the default 2.7 though?

    Perfect, then it will die sooner I guess


  • Mod

    @flopp said:

    What does that mean exactly? Write to eeprom when power disappear, how is that possible without power?

    Writing is not instantaneous. It might only take a millisecond, but during that time things can happen. So when the write starts, there is enough power. But half-way through the write power is lost. That leaves the eeprom in an unknown state.



  • @mfalkvidd
    Ok, I read it "to write as counting voltage drop" but it was "write to eeprom during voltage drop".
    It is now clear, thanks


  • Admin

    @flopp

    A more exotic "hack" could be to tap into the OBD2 interface, and for example check engine RPM to see if motor is running, or not.

    See this link..
    http://mechanics.stackexchange.com/questions/24171/how-to-detect-engine-ignition-on-off-status-using-obd2



  • @tbowmo
    Something like this?
    This looks really nice, something I want to try later on
    ELM327 + Arduino = Awesome DIY Car Gauge – 06:58
    — Justin Depew



  • My sketch seems to work, only problem is presentation in domoticz

    I will add a buzzer to now when it has reported to gateway


  • Admin

    @flopp

    There is an howto here http://www.instructables.com/id/Hack-an-ELM327-Cable-to-make-an-Arduino-OBD2-Scann/ looks pretty simple to get going, and read out the RPM's..



  • @tbowmo
    Nice, I will test in a few weeks



  • I have now solve the problem with storing data in Domoticz at correct day.

    I have another node that send back the same value as it is right now, every 2 hours



  • Version 1.12, changes gw.wait when asking for VAR from 5 sec to 1 sec. also added support for Buzzer on D3



  • This post is deleted!


  • Keep-alive version. I noticed when this Node didn't get any response from Controller it send back 0, which means that this day will be very many starts

    // Made by Daniel Nilsson
    // Tested with Domoticz 3.4967
    // Version 1.1
    // 2016-05-10
    
    //Keep-alive since Domoticz seems to not storing data if not data comes in for 3 hours(user setting)
    
    #include <SPI.h>
    #include <MySensor.h>
    
    #define CHILD_ID 0                          // Id of the sensor child
    #define NODE_ID 7                        // same ID as real Car Counter
    
    int Controller;                             // Current start counts from Controller, like Domoticz
    
    MySensor gw;
    MyMessage volumeMsg(CHILD_ID,V_RAIN);
    MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
    
    void setup()
    {          
      delay(500);   // wait for radio
      
      //Begin
      gw.begin(incomingMessage, NODE_ID);
      // Register this device as Rain sensor (will not show in Domoticz until first value arrives)
      gw.present(CHILD_ID, S_RAIN);       
    }
    
    void loop()
    { 
      
        //Ask for starts from Controller.
      
          Serial.println("Request start counts");
          //gw.request(CHILD_ID, V_VAR1);
          while (Controller == 0) {
            gw.request(CHILD_ID, V_VAR1);
            Serial.println("No response from Controller");
            gw.wait(60000);
            }
          gw.wait(5000);
          
    if (!resend((volumeMsg.set(Controller)), 5))return;
    gw.wait(1000);
    gw.sleep(120*60000); //less then timeout in Controller
    }
    
    // check if "st:fail" during gw.send, thanks n3ro
    bool resend(MyMessage &msg, int repeats)
    {
      int repeat = 1;
      boolean sendOK = false;
      int repeatdelay = 2000;
    
      while ((sendOK == false) and (repeat < repeats)) {
        if (gw.send(msg)) {
          sendOK = true;
        }
        else {
          sendOK = false;
          Serial.print("Error ");
          Serial.println(repeat);
        }
        repeat++; delay(repeatdelay);
      }
      if (sendOK == false && repeat == repeats){
        return false;
      }
      return true;
    }
    
    //Read if we have a incoming message.
    void incomingMessage(const MyMessage &message) {
      if (message.type==V_VAR1) {
        Controller = message.getULong();
        //pcReceived = true;
        Serial.print("Received start counts from Controller: ");
        Serial.println(Controller);   
      }
    }
    


  • Version 1.13 changed to fixed NODE_ID

    // Made by Daniel Nilsson
    // Tested with Domoticz 3.5721
    // 2016-12-10
    
    #include <SPI.h>
    #include <MySensor.h>
    
    #define CHILD_ID 0                          // Id of the sensor child
    #define NODE_ID 7                            // a number or AUTO to let controller assign
    #define SKETCH_NAME "Car start counter"     // Change to a fancy name you like
    #define SKETCH_VERSION "1.13"                // Your version
    
    int Controller;                             // Current start counts from Controller, like Domoticz
    boolean pcReceived = false;                 // If we have recieved the start counts from Controller or not 
    int starts;                                 // summary of all starts to be sent to Controller
    int eeprom;                                 // start counts read from/to be stored in EEPROM
    
    
    MySensor gw;
    MyMessage volumeMsg(CHILD_ID,V_RAIN);
    MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
    
    void setup()
    {          
      pinMode(3,OUTPUT);
      delay(2*45000);  // Allow time if USB/cigarette plug is powered before you turned the key
      digitalWrite(3,HIGH);
      delay(300);
      digitalWrite(3,LOW);
      delay(300);
      digitalWrite(3,HIGH);
      delay(300);
      digitalWrite(3,LOW);
      //Begin
      gw.begin(incomingMessage, NODE_ID, false);
      
      // Send the Sketch Version Information to the Gateway
      gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION);
    
      // Register this device as Rain sensor (will not show in Domoticz until first value arrives)
      gw.present(CHILD_ID, S_RAIN);       
      Serial.println("");
      eeprom = gw.loadState(0);                       // read EEPROM
      Serial.print(eeprom);                           // print EEPROM
      Serial.println(" starts have not been sent");
      Serial.println("add 1 start");
      Serial.print(eeprom);
      Serial.print("+1=");
      eeprom = eeprom + 1;
      Serial.println(eeprom);
      gw.saveState(0,eeprom);                         // store to EEPROM at position 0
      Serial.println("");
      
      Serial.println("Startup completed");
    }
    
    void loop()
    { 
      
        //See if we have the start counts from Controller - and ask for it if we dont.
        if (!pcReceived) {
          Serial.println("Request start counts");
          gw.request(CHILD_ID, V_VAR1);
          gw.wait(1000);
          return;
        }
    
    Serial.println("");
    eeprom = gw.loadState(0);                     // read EEPROM
    Serial.print(eeprom);
    Serial.println(" starts have not been sent");
    Serial.print(Controller);
    Serial.println(" starts from Controller");    
    starts = Controller + eeprom;                 // total starts
    Serial.print(eeprom);
    Serial.print("+");
    Serial.print(Controller);
    Serial.print("=");
    Serial.println(starts);
    Serial.print("Send ");
    Serial.print(starts);
    Serial.println(" to Controller");
    Serial.println("");
    
    if (!resend((volumeMsg.set(starts)), 6))return;
    if (!resend((lastCounterMsg.set(starts)), 6)) return;
    
    Serial.println("");
    Serial.println("store 0 to EEPROM");
    gw.saveState(0,0);                            // set 0 start to EEPROM, all have been sent
    Serial.println("sleep");                      // mission accomplished
    digitalWrite(3,HIGH);
      delay(900);
      digitalWrite(3,LOW);
      
    while(1){}
    
    }
    
    // check if "st:fail" during gw.send, thanks n3ro
    bool resend(MyMessage &msg, int repeats)
    {
      int repeat = 1;
      boolean sendOK = false;
      int repeatdelay = 2000;
    
      while ((sendOK == false) and (repeat < repeats)) {
        if (gw.send(msg)) {
          sendOK = true;
        }
        else {
          sendOK = false;
          Serial.print("Error ");
          Serial.println(repeat);
        }
        repeat++; delay(repeatdelay);
      }
      if (sendOK == false && repeat == repeats){
        return false;
      }
      return true;
    }
    
    //Read if we have a incoming message.
    void incomingMessage(const MyMessage &message) {
      if (message.type==V_VAR1) {
        Controller = message.getULong();
        pcReceived = true;
        Serial.print("Received start counts from Controller: ");
        Serial.println(Controller);   
      }
    }
    

Log in to reply
 

Suggested Topics

  • 8
  • 44
  • 2
  • 7
  • 3
  • 5

0
Online

11.4k
Users

11.1k
Topics

112.7k
Posts