I used millis to avoid blocking but, somehow it stops rocking

Hi Guys,

I'm working on a multi file sketch that leverages millis() to avoid blocking.

  1. I have a HC-SR04 setup to stop a motor when something is within 20cm. If the object is removed, the motor starts back up.
  2. I have a the step motor (288BYJ-48 5V DC 2106018457) that kicks off if a button is pressed. The step motor will then turn 180 degrees which takes 3 seconds.

If the button is pressed to trigger the step motor, the function for the HC-SR04 doesn't trigger the other motor to stop if something gets in the way during the 3 seconds the step motor is running. Once the step motor finishes, the HC-SR04 is then able to stop the motor due to the blockage.

I'm wondering if this is what it is or is there a way to get both functions to operate independently? The millis (I.e. non blocking strategy) I implemented, doesn't seem to fix the issue. Suggestions?
distanceWithAdds.ino (1.5 KB)
HC_SR04_check.ino (1.1 KB)
HC_SR04_flash.ino (1.2 KB)
setup.ino (473 Bytes)
stepMotor.ino (393 Bytes)

Per Railroader's suggestion, I'll review this other post. My gut tells me that once the step motor kicks off, it takes over the Arduino until it finishes the instruction (3 seconds). A non blocking strategy isn't going to help me with my problem. I'll tinker with my code some more before I accept this.

Thank you.

You have not been reading this topic: Latest Using Arduino/Project Guidance topics - Arduino Forum

Please do that and update Your post.

May we have a look?

Hey xfpd, I attached the files in the first post. The goal of the project is really just to incorporate multiple things that I've learned over the last year.

  • non blocking using millis()
  • having a sensor trigger one-two other events (E.g. flash LEDs, start/stop a fan/motor
  • I added push buttons to run a step motor. I'm learning that this addition won't free up the processor until it finishes 180 degree turn. This sort of makes since because the nano has to calculate the count down.
  • next I hope to add RFID as a requirement to make the step motor buttons respond but, running out of ports! :slight_smile:

Thanks for your interest in helping.

Why split the code into multiple files? You've just made something quite simple into something overly complicated and difficult to read/debug.

Since they are all *.ino files there is really no good reason.

I don't think many will go to the trouble of downloading and setting up the build you have.

a7

It would help to use Serial faster than the exceedingly slow 9600. Try 115200.

Also, try using New Ping's ping_timer (non-blocking) method. Note: it uses timer2 so will affect other operations using that timer. I haven't studied your code enough to know if that is a problem. Could be...

I think this is awesome. Make prototypes, master the code, move it to copper, maybe burn it to tiny (so you can get your Arduino back), then on to the next widget. Creating and solving is an enjoyable part of life, with every step a bit of learning. Keep it up.

Wrong. This is your issue. Indeed the Stepper - step() function is blocking.

" This function turns the motor a specific number of steps, at a speed determined by the most recent call to setSpeed() . This function is blocking; that is, it will wait until the motor has finished moving to pass control to the next line in your sketch. For example, if you set the speed to, say, 1 RPM and called step(100) on a 100-step motor, this function would take a full minute to run. For better control, keep the speed high and only go a few steps with each call to step() ."

You can get around this one of 2 ways:

  1. Set the speed very high and perform individual steps yourself.
  2. Use another library such as AccelStepper.

Thank you for the feedback guys. red_car, I broke the code into multiple files because I want to learn about breaking my project up into multiple files. For example because I'm trying to learn non blocking loops (I.e. avoid delay()) multiple files has forced me to use additional functions. Otherwise as alto777 pointed out, there is no good reason to do it.

ToddL1962, I went with your suggestion #1 which made sense and I got it to work! Because I'm using multiple files, I took the easy route and globally declared flag variables (maybe this is bad practice). The stepMotor() decrements the flag (from 256 > 0) each time the step function is called which turns the motor a bit. The step motor keeps turning until the global flag variables decrement to 0. If the activate step motor button is pressed again it starts all over. I put obstructions in front of the HC_SR04 while the step motor function kept getting triggered and it worked great (I.e. non blocking is working). You guys ROCK!

I'm thinking next I should implement pointers to those global variables, what do you suggest?

But that's pointless in this case. Normally you would split logic into separate components so they can be re-used... but all your code is tightly coupled... that is, although it's in seperate files, is all part of one piece of code... none of it works independently. You've just made things hard for zero benefit.

What exactly do you mean by that? What advantages do you envisage?

Hey red_car,
Thank you for your feedback. Regarding "Normally you would split logic into separate components so they can be re-used." I'm sure you are correct but, I don't understand. I'm learning. Regarding the global variables, I read in the C Programming book I'm reading that I should try to avoid declaring global variables when programming. I guess I'm wondering if any of you think I should have used pointers and passed them or some other strategy?

I posted a short video on YouTube and the source is on github.

Here's the log during a short demonstration:

currentTime - previousTime = 1000 // non blocking loop finishes fast, still 1 second
Distance: 60			  // HC_SR04 detects object 60 cm away, blink blue LED
currentTime - previousTime = 1000
Distance: 6			  // HC_SR04 detects object <20 cm away, stop fan & blue LED. Blink red LED until object is removed		
currentTime - previousTime = 1000
Distance: 16
currentTime - previousTime = 1000
Distance: 11			  // object still detected by HC_SR04
R button pushed			  // R button pushed, activat step motor process, set flagR = 256
flagR= 240			  // step motor runs, decrements flagR and exits function
flagR= 224			  // step motor runs, decrements flagR and exits function
currentTime - previousTime = 1087 // notice the time grew from 1000 to 1087 due to step motor activation
Distance: 62			  // object no longer detected by HC_SR04, activate fan, stop RED led and start blue LED
flagR= 208			  // step motor runs, decrements flagR and exits function
flagR= 192			  // etc..
flagR= 176			  // etc..
currentTime - previousTime = 1218 // notice time is growing for some reason...? 
Distance: 60			  // still nothing detected by HC_SR04
flagR= 160				
flagR= 144
flagR= 128
currentTime - previousTime = 1218
Distance: 63
flagR= 112
flagR= 96
flagR= 80
currentTime - previousTime = 1218
Distance: 62
flagR= 64
flagR= 48
flagR= 32
currentTime - previousTime = 1218
Distance: 63
flagR= 16
flagR= 0			  // stop turning step motor
currentTime - previousTime = 1000 // time drops down to 1000: step motor no longer being activated
Distance: 63
currentTime - previousTime = 1000
Distance: 73
currentTime - previousTime = 1000
Distance: 6			  // HC_SR04 detects object, stop blue blink, start red & stop motor
currentTime - previousTime = 1000
Distance: 6
currentTime - previousTime = 1000
Distance: 6
R button pushed			  // R button pushed to activate step motor, flagR set to 256
flagR= 240
flagR= 224
currentTime - previousTime = 1210 // time increased because step motor is active
Distance: 60			  // HC_SR04 doesn't detect object, start blue blink, stop red & start motor
flagR= 208			  // keep turning the step motor
flagR= 192
flagR= 176
currentTime - previousTime = 1219
Distance: 60				
flagR= 160
flagR= 144
flagR= 128
currentTime - previousTime = 1218
Distance: 60
flagR= 112
flagR= 96
flagR= 80
currentTime - previousTime = 1219
Distance: 64
flagR= 64
flagR= 48
flagR= 32
currentTime - previousTime = 1218
Distance: 60
flagR= 16
flagR= 0				// stop turning step motor,
currentTime - previousTime = 1000	// time returns to normal because step motor isn't active
Distance: 73
currentTime - previousTime = 1000
Distance: 74
currentTime - previousTime = 1000

Nice work. I am not in a a position to read your code just now, but the video is cool and I tots understand your combined pride and humility.

You have clearly gotten the first and possibly most important concept that allows getting the maximum mileage out of a tiny Arduino.

From the video, three things.

First, if that is a smoke detector nine volt battery powering everything? Thst may become a problem, one that might make trouble you don't first think is a power supply issue. If you need nine volts, 6 AAs will be better.

If you only need 5 volts, 4 AAs.

It's 2023, so rechargable, right? :wink:

Second, is that a little shutter on you laptop camera? I want one. I use a piece of HVAC foil tape. It hasn't and I don't think will fall off at the worst possible time, but it is a bit inconvenient when it comes to switching the camera on and off.

Lastly, I see a Rubik's Cube, or knockoff. If you are a wizard with it, good on you. If you are learning and getting better, also. I recommend that at a certain level of competence you switch all your algorithms and adopt their mirror image execution. This will scramble your brain a bit, always a good thing. More important it will distribute the wear and tear on your hands so they are evenly taxed. During the height of the pandemic I had to give up cubing. If I start taking it serious again I will try to learn a better method what takes fewer steps… and I never really cared about speed, I'm more of a contemplative type. I did set and pass a personal challenge, but mostly now I am just sitting with the cat and mindlessly solving, a toy with no batteries, no wires, no sensors or motors of any kind and… algorithms but zero code.

I look forward to reading your code and say without seeing it that pointers prolly don't need to enter into it, yet.

a7

I started to take a look at your code.

There is no *.ino file that has the tabs laid out. I should find a

arduino-non-blocking-practice.ino

in a folder with the same name.

So I started to go to a bit of trouble. It's that or sneak in some house cleaning before.

Fortunatley, making a *.ino (I called mine ANBP.ino and placed it in a folder with the same name) and placing all your *.ino files in that same folder lets the IDE automagically without trouble find and concatentate all what I thought would have to be laboriously "imported" with the tabbing mechanism.

Just one more vote against messing with tabs, especially if all you have are *.inos. None of the benefits of tabs when you use *.c or *.h or *.cpp accrue!

But right away this line in distaneWithAdhd.ino has an obvious error.

as we#include <NewPing.h> // HC-SR04 library

I fixed it, but the red ink was still spilling. Ffor now, I will meditate until my beach buddy rolls up, she who must not be kept waiting, and request that if you are to share with github, you do the conventional thing, which is to include a folder structured set of files suitable for clicking open in the IDE, which at that point should only be a problem for us if we haven't the same or all the libraries installed.

You will increase the attention the code gets by a few orders of magnitude (binary orders of magnitude).

Also, have you posted a schematic? It is never too soon to draw one, you may already be working from one. True we can easily reverse engineer one for ourselves, but that would be fraught and inefficient, for something you should do once, for yourself and for anyone else.

Hand drawn, take a picture and post it is actually probably the best. Be sure to include all components and connectios and take care to indicate exactly where all components are getting their power.

If you are new to schematic diagrams, google and just take a look at a few - my advice is to no get side tracked learning how to wrestle one out of a CAD package. Until you can comfortably draw a simple one like this is, or will be, it is not helpful to try to learn a CAD package at the same time.

TIA and HTH

a7

Hey alto777, Thank you for those suggestions! I had no idea about creating what you called the ANBP.ino. I have since added a "click_me_first.ino" and I removed that "as we" typo that snuck in there. Sorry about that but, thanks for the explanation! I also uploaded "non-blocking-schematic.jpg" which is a very poor picture of the setup that fortunately a took before pulling it all apart. The 9v battery was powering the step motor. The Nano took power from my laptop. In the future, I will create nicer schematics thanks to you.

I hope you didn't keep your beach buddy waiting. If you didn't, maybe add a laptop camera cover slide to your birthday gift list for next year. :wink: Glad it is warm where you are.

Regarding the Rubik's cube.... a source of frustration that continues to puzzle me.

I'm working on my next project now which involves using LoRa to transmit measurements. It is much more involved and will definitely be multi file/tabs!

One small tip in your ongoing quest for knowledge. People that respond to your posts won't get notified that you have replied unless you:

  • reply directly to their post, or
  • include a quote from their previous post, or
  • explicitly tag them in by using an @ in front of their name... like this @michael-1-1-1970

This is generally a good thing to do. You are better to try to encapsulate particular components within functions, and pass in/out variables. This way the function can be easily reused in another program, and the function itself is fully self contained making debugging much easier.

Having said that global variables can often makes things a lot simpler, so don't discount them altogether.

Pointers are not really the same concept. A pointer is simply a memory address, rather than a variable name. They can be extremely useful when you need them... but given you are starting out you probably don't need to consider them too much just yet.

Understanding how non-blocking code is probably one of the most fundamental concepts you need to understand when using the Arduino... looks like you have that under control. Nice work... and good video.

As per @alto777's comment, be careful powering too much from the Arduino itself... although it can supply some current for things like sensors and LED's... it is generally better to use a separate power supply for anything that draws more current.- like motors, solenoids.

One minor point...

I note you have pullup resistors on your push buttons. You can remove these completely from your circuit if you simply declare the buttons as...

pinMode(button, INPUT_PULLUP);

This will use the internal pullup resistor within the Arduino. Simple = Good.

Closer. One more try. Find the folder that you are actually using and zip that and put it on your github.

The main tab in what you did was just of stuff that does not compile.

Again, I put a teeny bit of effort in and changed it to be acceptable

click_me_first.ino

// empty main tab

/*
 
distanceWithAdds.ino  // this is where main() resides
HC_SR04_check.ino     // this checks to see if the HC_SR04 detects something within 20cm
HC_SR04_flash.ino     // this flashes the LEDS depending on what the HC_SR04 reports
setup.ino             // I moved the setup to a separate file
stepMotor.ino         // this runs constantly but, only moves the step motor if conditions are met

*/

Yet it does not compile. Only by emptying
     HC_SR04_check.ino     // this checks to see if the HC_SR04 detects something within 20cm
and  HC_SR04_flash.ino     // this flashes the LEDS depending on what the HC_SR04 reports

into

  distanceWithAdds.ino  // this is where main() resides

could I get an error free compilation. Oh the irony.

Your sketch uses 15 percent of the available resources, it is entirely pointless to use tabs that are *.inos. I will not tire of saying that. No advantage wahtsoever. The IDE editor is lame, but it has an adequate search tool - if you are tabbing to keep things straight in your head, just use text search to find anything. I have sketches that are quite larger. I usually don't use tabs, the one *.ino is a horrible mess I call "the soup", but if I need to look at the carrots, I can turn them up rapidly.

After nap time I will start looking at your code. :wink:

This


// check distance being read by HC-SR04 and start/stop motor, LEDs and \
    set stop value to over come HC-SR04 bugs

generates a warning - it's a multiline comment. Lose the backslash and put two salshes on the second line, that will silence the warning and is more common anyway.

Didn't see any warning? Go now to the IDE preferences and click "Show verbose output" and set compikler warnings to "All". For some brain-dead reason these are not the default settings so you may have been missing out on some helpful "suggestions". Read and heed the red ink, if anything doesn't make sense or you can't otherwise shut up a warning, google or ask here.


Off topic, but this

jeays.net/rubiks.htm

might be a good match for your learning style. When you get to it. An assuming you don't want to figure it out all by yourself. Obvsly there are ppl who have done, I was not one of them. :expressionless:

a7

Agree.

I am old fashioned and just plain old and usually use resistors. But using the internal pullups works just fine, and eliminates a few wires and components the accidental loss or moving of which might cause hard-to-find problems.

a7

Thank you @red_car for all of that. I will put the @ to good use.
I've done a lot with pointers in practice examples at the end of each chapter in my book but, I don't find myself implementing them much in my projects. The same with struct{}. For this reason, I look for opportunities to implement them. Probably I'm trying to hard.

got it on: pinMode(button, INPUT_PULLUP);

thanks again.

So much to learn...