Arduino Uno Programming Assistance Request

Advert

Arduino Uno Programming Assistance Request

Home Forums Electronics in the Workshop Arduino Uno Programming Assistance Request

Viewing 25 posts - 1 through 25 (of 86 total)
  • Author
    Posts
  • #334741
    James Alford
    Participant
      @jamesalford67616

      Good morning.

      I wonder whether anyone can help with a question on programming an Arduino Uno?

      I am writing a programme to operate a mechanical clock mechanism. The actual time is set by a plug-in RTC module. I want to make
      the clock strike each hour, once at one o'clock, twice at two o'clock and so on. I am struggling with this part of the code. I have written one sequence
      that works, but it is extremely cumbersome, so am trying to use a loop to make it more efficient.

      There are two code snippets below, both trying to operate the LED five times.

      Snippet One. This is what I am trying to use to make an LED flash five times, without using a "delay" function, and then to remain off. It turns on, but remains on.

      Snippet Two. I have added code to try and make the LED turn off again after a preset duration. However, the LED does not turn on at all with this. All of the variables and constants have been defined. The same sequence to turn off the LED works elsewhere in the programme, but I cannot see why it does not work here.

      I am going around in circles and should appreciate any constructive suggestions, including an alternative way to achieve the same objective.

      Thank you.

      James.
      Snippet One
      void Strike_the_Hour () {
      unsigned long Present_Each_Hour_Strike_Time = millis(); // Sets the present time
      Elapsed_Each_Hour_Strike_Time = Present_Each_Hour_Strike_Time – Previous_Each_Hour_Strike_Millis; // Calculates length of time that has passed since last event

      while(number_of_strikes < 5){

      State_of_Hours_Activator_LED = HIGH; // Activates the solenoid.
      digitalWrite(Hours_Activator, State_of_Hours_Activator_LED);

      number_of_strikes ++;
      }
      }

      Snippet 2
      void Strike_the_Hour () {
      unsigned long Present_Each_Hour_Strike_Time = millis(); // Sets the present time
      Elapsed_Each_Hour_Strike_Time = Present_Each_Hour_Strike_Time – Previous_Each_Hour_Strike_Millis; // Calculates length of time that has passed since last event

      while(number_of_strikes < 5){

      State_of_Hours_Activator_LED = HIGH; // Activates the solenoid.
      digitalWrite(Hours_Activator, State_of_Hours_Activator_LED);

      if (Elapsed_Each_Hour_Strike_Time >= Period_Each_Hour_Strike_On) {

      State_of_Hours_Activator_LED = LOW; // Activates the solenoid.
      digitalWrite(Hours_Activator, State_of_Hours_Activator_LED);

      number_of_strikes ++;
      }

      }
      }

      Advert
      #31921
      James Alford
      Participant
        @jamesalford67616

        Struggling with While Loop

        #334747
        SillyOldDuffer
        Moderator
          @sillyoldduffer

          Hi James,

          I think I can see a couple of logic flaws in Snippet 2, but before I comment can you explain why you don't want to use delay()? It might be important.

          delay() is the easiest way to solve the problem as stated BUT are you agin it because your Arduino program needs to keep going whatever strike_the_hour() is doing? That is to continue working on something else whilst striking rather than going to sleep in delay(). If so the answer is a bit more complicated and I don't want to confuse by explaining unless it's relevant.

          Dave

          #334749
          duncan webster 1
          Participant
            @duncanwebster1

            Hi James,

            I've done exactly what you're trying to achieve, but as SOD says it's a lot easier using delays. The mechanism uses the old clock pin barrel driven by a stepper to ring the quarters, the hours are bonged by a solenoid

            void loop()
            {
            if(digitalRead(QPin) == LOW && QPinWasHigh)
            {
            QPinWasHigh = false;
            chime();
            }

            if(digitalRead(HPin) == LOW && HPinWasHigh)
            {
            HPinWasHigh = false;
            ringHours();
            }
            }

            void ringHours()
            {
            int i;
            for (i = 1; i <= hours; i++)
            {
            digitalWrite(BongPin, HIGH);
            delay(150);
            digitalWrite(BongPin, LOW);
            delay(1000);
            }
            hours++;
            if (hours > 12)
            hours = 1;
            HPinWasHigh = true;
            }

             

            Edited By duncan webster on 01/01/2018 12:11:29

            #334751
            SillyOldDuffer
            Moderator
              @sillyoldduffer

              Posted by duncan webster on 01/01/2018 11:39:55:

              … the hours are boned by a solenoid …

              I hope that's not a euphemism Duncan!

              Happy New Year

              #334753
              James Alford
              Participant
                @jamesalford67616

                Dave:

                Thank you for the prompt reply and I should appreciate your observations on the second snippet.

                As you guess, there are other operations going on that would be affected by using a delay. The clock, only partly designed so far, will use solenoids to operate seconds, minutes, hours, days, quarter strike and hourly strike mechanisms. I did try using a delay function, but it halted all of the other operations whilst waiting.

                I have the quarter strike code working (I have built a test board with LEDs), but it is extremely crude and cumbersome.

                Duncan:

                Thank you for the suggested code, which I shall study and see whether I can adapt it.

                Regards,

                James.

                #334754
                duncan webster 1
                Participant
                  @duncanwebster1

                  Just realised that's not the latest code, current edition doesn't have an HPin (hours) it counts the quarters, and after it's struck the quarters it bongs (thanks SOD!) the hours. Unfortunately due to my chaotic filing system I can't find the latest code. This should however give you the idea.

                  #334766
                  SillyOldDuffer
                  Moderator
                    @sillyoldduffer

                    Hi James,

                    Have a look at this example. It bongs 5 times each time bongWanted is set true (only once in the example).

                    It uses the Timer_master library, which is described here and loaded from the IDE's Sketch->Include Library menu. (It's near the end of the list.)

                     

                    #include
                    #include

                    const char LED_pin = 13;
                    bool bongWanted = true;
                    int pulseLength_ms = 1000;

                    Timer t;
                    int numberOfBongs = 5;

                    void setup() {
                      // put your setup code here, to run once:
                      pinMode( LED_pin, OUTPUT );
                      digitalWrite( LED_pin, LOW );
                    }

                    void loop() {
                      // put your main code here, to run repeatedly:
                      t.update();

                      if (bongWanted)
                      {
                         bongWanted = false;
                         t.oscillate( LED_pin, pulseLength_ms, LOW, numberOfBongs );
                      }

                    }

                     

                    The Timer library uses call-backs, not delay(). Apart from a few microseconds spent running t.update() each time loop() is entered, the overhead is low. When t.oscillate is called, in effect it detaches from the main program which carries on without it, leaving oscillate() to do its thing. (Although it looks as if the Arduino is running two programs at once, what actually happens is that the main program is interrupted whatever it happens to be doing by oscillate(). Having grabbed control, oscillate() toggles the LED, and then allows the main program to carry on from the point it was interrupted. If you use interrupts, you have to be careful to keep them quick, or the main program may lose the plot.)

                    Please ask if this doesn't make sense!

                    Dave

                    Sorry about the edits, I thought I knew how to make the forum print source and it didn't quite work.  Ho hum.

                    The broken #includes load Event.h and Timer.h

                    Edited By SillyOldDuffer on 01/01/2018 13:45:45

                    Edited By SillyOldDuffer on 01/01/2018 13:46:07

                    Edited By SillyOldDuffer on 01/01/2018 13:46:42

                    Edited By SillyOldDuffer on 01/01/2018 13:48:13

                    Edited By SillyOldDuffer on 01/01/2018 13:50:27

                    #334768
                    James Alford
                    Participant
                      @jamesalford67616

                      Dave,

                      Thank you for this code, which I shall have a play with. May I just ask, though: when using oscillate, does the LED light up for the defined period (1,000ms in this example), then extinguish for a 1,000ms for five cycles?

                      Regards,

                      James.

                      #334774
                      SillyOldDuffer
                      Moderator
                        @sillyoldduffer
                        Posted by James Alford on 01/01/2018 14:09:09:

                        Dave,

                        Thank you for this code, which I shall have a play with. May I just ask, though: when using oscillate, does the LED light up for the defined period (1,000ms in this example), then extinguish for a 1,000ms for five cycles?

                        Regards,

                        James.

                        Hi James,

                        Yes that's right, oscillate() has an equal on/off duty cycle. If that's not what's needed, there's a pulse() function which gives you the necessary control.

                        I chose not to use pulse() in the example because it's more complicated to use and the extra code makes it less obvious what timer t is doing. You have to write code to create the inter-pulse gaps and the number of repetitions. It's not difficult and there's more than one way of doing it, for example by using t.after() to control when call t.pulse() is called.  I can knock up an example if it helps.

                        Dave

                        Edited By SillyOldDuffer on 01/01/2018 14:45:30

                        #334777
                        James Alford
                        Participant
                          @jamesalford67616

                          Dave,

                          Thank you. The pulse function would be more in line with what I am trying to achieve. I should like to activate the solenoid briefly to strike the chime, then pause it for a longer period to allow it to ring, before striking again. This will also conserve battery life.

                          I shall have a play with both oscillate and pulse, but should be grateful for any examples that you can provide.

                          Regards,

                          James.

                          #334779
                          SillyOldDuffer
                          Moderator
                            @sillyoldduffer

                            Posted by James Alford on 01/01/2018 14:53:07:.

                            …but should be grateful for any examples that you can provide.

                            I need to stretch my legs first but watch this space.

                            Dave

                            #334794
                            SillyOldDuffer
                            Moderator
                              @sillyoldduffer

                              OK I think this works.

                              • In setup(), a timer calls 'doBongPulse' repeatedly.
                              • The pulse can be any shape, the example is ON 100mS, OFF 1000mS
                              • doBongPulse only emits a pulse if the boolean chiming has been set true in the main program
                              • doBongPulse only emits the number of pulses specified in numberOfBongs
                              • doBongPulse is triggered simply by setting chime=true anywhere in the main program.

                              The code:

                               

                              #include <Event.h>
                              #include <Timer.h>

                              const char LED_PIN = 13;
                              long pulseLength_ms = 100, interPulseTime_ms = 1000; // Defines the pulse on and off times

                              Timer t;
                              int numberOfBongs = 0;
                              volatile int bongCount = 0;    // variables updated by an interrupt function must be volatile
                              volatile bool chiming = false;

                              long timeNow;  // for demo

                              void setup() {
                                // put your setup code here, to run once:
                                pinMode( LED_PIN, OUTPUT );
                                digitalWrite( LED_PIN, LOW );
                                t.every( pulseLength_ms + interPulseTime_ms, doBongPulse );  // Ask to send pulses continually
                               
                                Serial.begin(9600);  // for demo
                                timeNow = millis();  // for demo
                              }

                              void loop() {
                                //  This example emits 5 bongs once every 20 seconds
                               
                                t.update();

                                if ( millis() - timeNow > 20000 )  // Set chiming true every 20 seconds
                                {
                                  timeNow = millis();

                                  numberOfBongs = 5;
                                  chiming = true;  // setting chiming true causes doBongPulse() to output numberOfBongs pulses  
                                }
                               
                                Serial.println( millis() );  // For demo Proves chiming doesn't interfere with other work
                              }

                              void doBongPulse( )
                              {
                                // Although called continually, pulses are only sent when chiming is true.
                                // chiming stops after the required numberOfBongs have been sent
                                // To use set numberOfBongs = n and chiming = true in the main program
                                // Don't set chiming true again until the current bongs have finished!

                                if ( chiming )
                                {
                                  if ( bongCount < numberOfBongs  )
                                  {
                                    // OK to send a bong
                                    t.pulse( LED_PIN, pulseLength_ms, LOW );
                                    bongCount++;
                                  }
                                  else
                                  {
                                    // Stop Chiming - the sequence is finished
                                    chiming = false;
                                    bongCount = 0;
                                  }
                                }
                              }

                               

                              Fingers crossed. It works ok on my Uno.

                              Dave

                              Edited By SillyOldDuffer on 01/01/2018 16:43:09

                              Edited By SillyOldDuffer on 01/01/2018 16:44:01

                              Edited By SillyOldDuffer on 01/01/2018 16:46:12

                              Edited By SillyOldDuffer on 01/01/2018 16:46:44

                              #334812
                              Marcus Bowman
                              Participant
                                @marcusbowman28936

                                Just a thought. What has been proposed so far are good workable bits of code.

                                You mentioned earlier that your were reading the time from an RTC module. Does it, by any chance, make use of an interrupt on the Arduino? I ask because the interrupt will interfere with the millis() function. At the very least a long ISR will interfere with the count required for the value returned by millis().

                                Which library, if any, are you using with the RTC module?

                                Marcus

                                #334815
                                James Alford
                                Participant
                                  @jamesalford67616

                                  Dave:

                                  Thank you for the latest code example. I have been playing with the oscillate, which is working as it is meant to. I also had a play with pulse, but with little success. I shall have a good read of the example that you have just posted and let you know how I get on.

                                  Marcus:

                                  I have to be honest: I am not sure whether I am using an interrupt or not, but as far a sI am aware, I am not doing so. The module is a DS 3281 and I am using the libraries DS3231.h.

                                  Regards,

                                  James.

                                  #334821
                                  Neil Wyatt
                                  Moderator
                                    @neilwyatt

                                    Looking at the original code, I don't see number_of_strikes being initialised.

                                    Is it a global variable initialised elsewhere?

                                    If not global, it is probably being set to zero every time you enter the function so zero bongs is what you would get.

                                    Neil

                                    #334854
                                    James Alford
                                    Participant
                                      @jamesalford67616

                                      Neil,

                                      I defined the variable number_of_strikes at the start of the programme and set it to zero. I did code the loop to print to screen the value of this variable and it was increasing to five and stopping at five correctly. What I could not make happen was to make the LED flash within the loop.

                                      Progress will now slow down again as I am back to work today.

                                      Regards,

                                      James.

                                      #334862
                                      Marcus Bowman
                                      Participant
                                        @marcusbowman28936

                                        James,

                                        Looking again at snippet 1, and working on the basis that it is always useful to know why something has gone wrong, I can't see where you do a LOW digitalWrite. Surely once the led is turned on, it needs to be turned off again after a period of time. Otherwise, it will stay on until you go around the loop again and turn it on again….and then it will never be off.

                                        Snippet One
                                        void Strike_the_Hour () {
                                        unsigned long Present_Each_Hour_Strike_Time = millis(); // Sets the present time
                                        Elapsed_Each_Hour_Strike_Time = Present_Each_Hour_Strike_Time – Previous_Each_Hour_Strike_Millis; // Calculates length of time that has passed since last event

                                        while(number_of_strikes < 5){

                                        State_of_Hours_Activator_LED = HIGH; // Activates the solenoid.
                                        digitalWrite(Hours_Activator, State_of_Hours_Activator_LED);

                                        :::::::so this turns it ON, but for how long? And where is it turned OFF again before you go around the loop again?

                                        Even if it did go off, it will go on and off so quickly you will not see the off states.

                                        SOD's program sets a specific delay time for that purpose, so that's one way of sorting the problem.

                                        number_of_strikes ++;
                                        }
                                        }

                                        #334873
                                        Geoff Theasby
                                        Participant
                                          @geofftheasby

                                          "Debonging" what a wonderful word!

                                          Dem bongs, dem bongs, dem debongs,

                                          etc.,

                                          De mainspring is connected to the geat wheel,

                                          the great wheel is connected to the fusee,

                                          the fusee is connected to the pinion,

                                          the pinion is connected to the 'scapement,

                                          now hear de word of de bong!

                                          Geoff

                                          #335024
                                          James Alford
                                          Participant
                                            @jamesalford67616

                                            Marcus,

                                            Thank you for the observation about snippet one. It is obvious, now that you point it out, why that code never worked. Thank you.

                                            In snippet two, I was trying to use State_of_Hours_Activator_LED = HIGH; to turn the LED on. It should then stay on for 150 ms, set earlier on in the programme in the variable Period_Each_Hour_Strike_On.

                                            The next sequence — if (Elapsed_Each_Hour_Strike_Time >= Period_Each_Hour_Strike_On) {State_of_Hours_Activator_LED = LOW; digitalWrite(Hours_Activator, State_of_Hours_Activator_LED);

                                            calculates how long the LED has been lit and turns it off again. The while(number_of_strikes < 5) and while(number_of_strikes++ is meant to repeat it all.

                                            Looking at it again, though, I think that the problem is to do with me not clearing or resetting the time variables correctly, meaning that the calculation is not working. The sequence works elsewhere in the programme, but, there, it only needs to work once, not repeatedly, hence not needing to be reset.

                                            Regards,

                                            James.

                                            Edited By James Alford on 02/01/2018 21:47:57

                                            #335066
                                            James Alford
                                            Participant
                                              @jamesalford67616

                                              Dave: just a quick update. I have integrated your "pulse" code into my programme, adjusting it to activate pin 12, rather than 13 as 13 is in use already. It does exactly what I want it to do. I now need to write the sequences to trigger the pulsing after the final quarter has been struck on the hour. This should be fairly straightforward……….. famous last words: watch this space.

                                              Regards,

                                              James.

                                              #335086
                                              SillyOldDuffer
                                              Moderator
                                                @sillyoldduffer

                                                Hi James,

                                                The root cause of your problem with Snippet is that it's difficult to do it that way. I had a go yesterday and got into a right tangle; the trouble is the program has to keep track of the number of strikes, the pulse on-time, and the pulse off-time. It's a sort of 'State Machine' and, the logic not being sequential, they use a different design approach. At the moment my attempt only does long slow flashes and I'm baffled as to why – although the logic looks right to me, it obviously isn't!

                                                Last night in bed I had a flash of inspiration – a way of simplifying the code that manages pulse lengths. If it works I'll post it. But, even if it does, I think the timer method is better. It has the advantage of being expandable; for instance it would be fairly easy to add another timer function to do quarters.

                                                Marcus made an interesting point about interrupts interfering with millis(). That's one of several reasons an Arduino doesn't make a good clock in it's own right – up to 20 seconds out per day. I had a look at the RTC library and it doesn't seem to use interrupts. But, even if it did, in your application, the Arduino is only striking the bongs, which aren't time critical.

                                                Dave

                                                #335095
                                                Frances IoM
                                                Participant
                                                  @francesiom58905

                                                  my top level description would be

                                                  Init system
                                                  loop1:
                                                  Wait for external RF clock
                                                  loop2:
                                                  set State as appropriate based on RF clock
                                                  call Statemachine;
                                                  wait tick;
                                                  if State = 0 sleep(until next RF clock interrupt etc) else goto loop2;
                                                  goto loop1;

                                                  Statemachine:
                                                  action = act_table(state);
                                                  do action;
                                                  state = nextstate_table(state);
                                                  exit;

                                                  idea is that tick set to say 100ms which is time solenoid is on followed by say 2 periods off then to strike 2 then state sequence/action table entering at state S (states sequence down table) and exiting with state 0 is
                                                  next state : action
                                                  S-1 : on
                                                  S-2: off
                                                  S-3: off
                                                  S-4: on
                                                  S-5: off
                                                  0 : off

                                                  likewise bongs for qtrs start at a different state with maybe driving a different solenoid

                                                  simple table lookups save a vast amount of tangled code – was using these to control telephone exchanges in early 70s based on pioneering Japenese softwate

                                                  #335107
                                                  duncan webster 1
                                                  Participant
                                                    @duncanwebster1

                                                    If the RTC is a DS3231 it outputs a 1 hz square wave. You could use this to drive the seconds solenoid direct (via some amplification) and to trigger a counting interrupt on the Arduino. The Arduino can then do all the bell ringing, move the minute hand etc, as a full chime sequence (28 strikes) takes less than 1 minute so you're not trying to do 2 things at once

                                                    #335121
                                                    Peter Bell
                                                    Participant
                                                      @peterbell11509

                                                      I'm watching this thread with great interest to see how it eventually works out as I'd like to be more familiar with Arduino .

                                                      I've got a solenoid chime working using a Picaxe triggered from a quartz movement pointer and an 80ms pulse produces a satisfying "bong"

                                                      I use loops and compare an incremented master number to a counted number to give the correct strike, clear the incremented number after each performance and reset the master number after 12.

                                                      Peter

                                                    Viewing 25 posts - 1 through 25 (of 86 total)
                                                    • Please log in to reply to this topic. Registering is free and easy using the links on the menu at the top of this page.

                                                    Advert

                                                    Latest Replies

                                                    Viewing 25 topics - 1 through 25 (of 25 total)
                                                    Viewing 25 topics - 1 through 25 (of 25 total)

                                                    View full reply list.

                                                    Advert

                                                    Newsletter Sign-up