Related
So I need some help reviewing and editing the code below. Is there a way to make it more efficient instead of what I did? Also, are there better ways to do some of the methods I did? Answers are appreciated greatly (I'm fairly new to Arduino).
The code below is for a corridor ledstrip that lights up when the distance sensor on the Arduino detects anything below a certain distancenear it (yes, I thought of using a motion sensor but I only had distance sensors on hand).
const int trigPin = 9;
const int echoPin = 10;
float duration, distance;
#include <FastLED.h>
#define LED_PIN 6
#define NUM_LEDS 60
int timingnum = 0;
//#define BRIGHTNESS bright
int bright = 100;///the brightness of the leds
int lednum = 60;///the number of leds available
int timer;
CRGB leds[NUM_LEDS];
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
// FastLED.setBrightness( BRIGHTNESS );
}
void loop() {
lights();
}
void sensor() { ///THIS IS CLEAN
digitalWrite(trigPin, LOW);
delayMicroseconds(timingnum);
digitalWrite(trigPin, HIGH);
delayMicroseconds(timingnum);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;
Serial.print("Distance: ");
Serial.println(distance);
}
// in the lights the reason why it wouldnt output the data for the
// represented while loops is because the while loop continued running
// without the data from the distance and thus it needs to constantly
// be updated in the while loop conditional statments
void lights() {
sensor(); //data update
while (distance <= 10) {
for (int x = 0; x <= 60; x++) {
leds[x] = CRGB(255,255,255);
FastLED.show();
sensor(); //sensor update
}
for (timer = 0; timer <=800; timer++) {
sensor();
}
sensor(); //replaces the data updates above
}
sensor(); //sensor update
while (distance >= 11) {
for (int x = 0; x <= 60; x++) {
leds[x] = CRGB(0,0,0);
FastLED.show();
}
sensor(); //data update
}
}
I would be tempted to make a little function to set the RGB colors of your LED strip: void fillup(byte r, byte g, byte b). Then you would call it in two places: fillup(0,0,0) and fillup(127,127,127) (yeah, not 100% on).
Also, I'm confused why you have so many sensor() calls in the first while loop. Seems like you only need to call it once when you need updated values for distance.
Also, the comments on the sensor calls are confusing... after reading them I have no idea what sensor is supposed to do. I tend to put a comment before functions that describes what they do, and put a stand-alone comment in the code to explain what the next "paragraph" does. And I avoid banners - they get in the way.
Do you want FastLED.show() inside the loop that updates the colors? Or just after the loop, to update the hardware after the array is changed? IDK, just asking.
I usually do not mind globals, but in this case you would be better off letting sensor return the distance. Then you could while ( sensor() <= 10 ). Or use a do.. while loop with one call to sensor at the bottom if you want to keep the global.
I'd try to get rid of the floating point, too... just calculate what your thresholds are in the raw echo values, and use those. Do we really care what the internal units of measurement are?
Sorry for the long unload... There's nothing wrong with what you have.
Stumbling my way thru a problem where I'm trying to move two or more servos at the same time while iterating through multiple arrays. Think of a drum machine, where I may want to hit a snare, bass and high hat all the same time on some beats. Not on others.
byte bass[] = {1,0,1,0};
byte snare[] = {1,1,0,0};
byte hihat[] = {1,1,0,0};
for (int i = 0; i < sizeof(snare); i++)
{
if (i == 1)
{
snare.write(45);
delay(250);
snare.write(0);
dleay(250);
}
else if
{
snare.write(0);
delay(500);
}
}
And so on for each array, each instrument.
As you may see, this won't work because of the delay() and that the two or more loops wouldn't trigger at the same time as the procedural nature.
I understand I could pull this off with some wizardry using millis() instead of using delay().
Tho I'm curious if using a servo driver board would be easier to accomplish what I'm out to do or is millis() the only avenue for me?
Update
After tinkering with millis() for a while, I found myself overcomplicating the task and found a 'close enough' for the task I had in mind by going a simplier route.
#include <Servo.h>
Servo s1;
Servo s2;
Servo s3;
Servo s4;
byte bass[] = {45,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,45,0,45,0,0,0,0,0,0,0,0,0,45,0,0,0};
byte snare[] = {0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0};
byte hihat[] = {45,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0};
byte cymb[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0};
void setup() {
Serial.begin(9600);
s1.attach(6);
s2.attach(5);
s3.attach(4);
s4.attach(3);
s1.write(0);
s2.write(0);
s3.write(0);
s4.write(0);
}
void loop()
{
drummer();
}
void drummer()
{
for (int i = 0; i < sizeof(bass); i++)
{
s1.write(bass[i]);
s2.write(snare[i]);
s3.write(hihat[i]);
s4.write(cymb[i]);
delay(250);
}
}
Tho I'm curious if using a servo driver board would be easier to
accomplish what I'm out to do or is millis() the only avenue for me?
Millis() will work. I don't own a servo driver board so I can't say about it but I don't think that would help in running servos simultaneously.
Here is the code using millis(). I just wrote it based on your given example.
byte bass[] = {1,0,1,0};
byte snare[] = {1,1,0,0};
byte hihat[] = {1,1,0,0};
usigned long currentMillis = 0;
unsigned long previousMillis = 0;
const long interval = 250;
const long longInterval = 500;
for (int i = 0; i < sizeof(snare); i++)
{
if (i == 1)
{
snare.write(45);
currentMillis = millis();
if (currentMillis - previousMillis >= interval){
snare.write(0);
}
}
else if
{
if (currentMillis - previousMillis >= longInterval){
snare.write(0);
}
}
}
currently am working on project to open a door with access code using arduino UNO and a servo motor. Normal operation requires entering access code using keypad which is working fine. Another option requires pressing a button that causes an interrupt to rotate the servo motor. My problem is my interrupt only works once and never works again. Plus how do i put the for-loop to rotate the servo motor inside the interrupt function with a delay. I know that is not possible but am calling another function that has the delayMicroseconds but all this is not working. Below is my implementation please help
#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Servo.h>
Servo servo;
const int openButtonPin = 2;
void setup() {
// put your setup code here, to run once:
servo.attach(5);
pinMode(openButtonPin, INPUT); //Pin 2 is input
attachInterrupt(0, enforceOpenAccess, HIGH); // PIN 2
}
void(* resetFunc)(void) = 0;
void loop()
{
//My other keypad implementations go here
}
void myDelay(int x) // function to cause delay in the interrupt
{
for(int i = 0; i<x; i++)
{
delayMicroseconds(1000);
}
}
void enforceOpenAccess() // ISR
{
for(int k =0; k<=180; k+=2)
{
servo.write(k); //rotate the servo
myDelay(30); //delay the rotation of the servo
}
}
The code above is run on arduino UNO being simulated in proteus and the interrupt button is a push button. Please if there is other ways of implementing that but with the same behaviour as I have described above help out. Thanks a lot
There are a couple of problems in the slice of code you posted. Just for completeness, you should post the loop function, since we can't guess what you wrote inside.
Just one comment: did you put a pullup? Otherwise use INPUT_PULLUP instead of INPUT for the button pinmode.
The main one is that you attached the interrupt for the HIGH mode, which will trigger the interrupt any time the pin is up, not on the rising edge. And please use the macro digitalPinToInterrupt to map to the correct pin:
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
Then.. Let's improve the code. You really should use the interrupts only when strictly necessary when you have to respond IMMEDIATELY (= less than a couple of milliseconds) to an input. Here you don't have to, so it's MUCH better to check for the button in the loop (more on turning the motor following)
uint8_t lastState;
void setup()
{
...
lastState = LOW;
}
void loop()
{
uint8_t currentState = digitalRead(openButtonPin);
if ((currentState != lastState) && (currentState == HIGH))
{
// Start turning the motor
}
lastState = currentState;
...
}
This will enable you to properly debounce the button too:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT); //Pin 2 is input
debouncer.attach(openButtonPin);
debouncer.interval(5); // interval in ms
}
void loop()
{
debouncer.update();
if (debouncer.rose())
{
// Start turning the motor
}
...
}
If, on the other way, you REALLY want to use the interrupts (because waiting for a couple of milliseconds is too much for you), you should do something like this:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT);
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
}
void loop()
{
...
}
void enforceOpenAccess() // ISR
{
// Start turning the motor
}
It looks like your code? No, because now we'll speak about turning the motor
You should NOT use delays to make steps, because otherwise you will wait for 30ms * 180 steps = 5.4s before being able to do anything else.
You can, however, make a sort of reduced state machine. You want your servo to move from 0 to 180 in steps of 1. So let's code the "don't move" state with any value greater than 180, and consequently we can do something like this in the loop:
unsigned long lastServoTime;
uint8_t servoPosition = 255;
const int timeBetweenSteps_in_ms = 30;
void loop()
{
...
if (servoPosition <= 180)
{ // servo should move
if ((millis() - lastServoTime) >= timeBetweenSteps_in_ms)
{
lastServoTime += timeBetweenSteps_in_ms;
servoPosition++;
if (servoPosition <= 180)
servo.write(servoPosition);
}
}
}
Then, using any of the previous examples, instead of // Start turning the motor write
lastServoTime = millis();
servoPosition = 0;
servo.write(servoPosition);
This way you won't block the main loop even when the button is pressed
This is what is in my loop()
char key = keypad.getKey();
if(key)
{
if(j < 10)
{
studentNumber[j] = key;
//holdMaskedNumber[j] = '*';
lcd.setCursor(0,2);
lcd.print(String(studentNumber));
if(j == 9)
{
studentNumber[9] = '\0';
//holdMaskedNumber[9] = 0;
lcd.clear();
//String number = String(studentNumber);
//lcd.print(number);
//delay(1000);
//lcd.clear();
lcd.print("Access Code");
}
j++;
}
else
{
if(i < 5)
{
accessCode[i] = key;
holdMaskedCode[i] = '*';
lcd.setCursor(1,2);
lcd.print(String(holdMaskedCode));
if(i == 4)
{
holdMaskedCode[5] = '\0';
accessCode[5] = '\0';
//lcd.clear();
//lcd.setCursor(0,0);
//accessCodeString = String(accessCode);
//lcd.print(accessCodeString);
//delay(1000);
lcd.clear();
for(int i =0; i<6; i++)
{
lcd.print("Please wait.");
delay(500);
lcd.clear();
lcd.print("Please wait..");
delay(500);
lcd.clear();
lcd.print("Please wait...");
delay(500);
lcd.clear();
}
digitalWrite(4, HIGH);
lcd.print("Access Granted");
for(int k =0; k<=180; k+=2)
{
servo.write(k);
delay(30);
}
resetFunc();
}
i++;
}
}
}
I was using Arduino but I encountered problems with my code. I want a button to illuminate certain lights, and every time you press it, the lights will illuminate in a different pattern. Right now, the code does not keep track of how many times the button has been pressed. I would really appreciate any help!
const int buttonPin = 4; // pushbutton 1 pin
int ledPins[] = {5,6,7,8,9,10};
int buttonCounter = 0;
int buttonState = digitalRead(buttonPin);
void setup(){
pinMode(buttonPin, INPUT);
Serial.begin(9600);
// The for loop refers to each pin number
// by their position in the array
for(int i = 0; i < 6; i++){
pinMode(ledPins[i], OUTPUT);
}
}
int countButtonPresses()
{
if (buttonState == 0)
{
buttonCounter = buttonCounter + 1;
}
return buttonCounter;
}
void loop(){
int displayTime = 800;
int pattern = countButtonPresses();
Serial.println(pattern);
switch(pattern) {
case 1:
for(int i = 0; i <=5; i++){
digitalWrite(ledPins[i], HIGH);
delay(displayTime);
digitalWrite(ledPins[i], LOW);
}
break;
case 2:
for(int i = 0; i <=5; i += 2){
digitalWrite(ledPins[i], HIGH);
delay(displayTime);
digitalWrite(ledPins[i], LOW);
}
break;
default:
Serial.println(countButtonPresses());
}
}
In my opinion, the problem is that you don't have a clear idea of what you want to do :-)
The first error is logical: you cannot count the number of button presses, because when you have pressed the button once, the program returns "one", and executes the pattern one. The next time you press the button, the program returns "one" again, and so on. So, you have to change your logic (for example, when you press the first time, execute pattern one, when you press the second time, execute the pattern two).
The second error is the handling of the button press. Assuming that your button is pressed if it returns zero (we say "active low"), probably your program will count a few dozens of times before you release it. The cause of this is in the fact you don't have "debounced" it.
The "bouncing" effect is that your Arduino is faster than you, and if you press a button for a tenth of a second, Arduino loops several times finding the button pressed and counting many "presses".
To avoid this, when you find it pressed (buttonstate = 0), you must perform the illumination task, and wait until buttonstate becomes != 0. Also you must move the instruction "buttonstate = digitalRead(buttonPin);" within the loop() function in order to read continuously the status of the button.
I "dumped" some ideas to make it work; try to write it better, and if there are problems, ask again.
I modified your code a bit. I added a simple interrupt which modifies the count no matter where your code pointer is currently in (this will enable you to store number of presses even if you are inside a loop).
int buttonPin = 2; // pushbutton 1 pin
int ledPins[] = {5,6,7,8,9,10};
volatile int buttonCounter = 0;
int buttonState = 0;
void countButtonPresses();
void ExecuteIllumination(int ledPins[6],int DelayTime,int increments);
void setup(){
pinMode(buttonPin, INPUT);
Serial.begin(9600);
// The for loop refers to each pin number
// by their position in the array
for(int i = 0; i < 6; i++){
pinMode(ledPins[i], OUTPUT);
}
//Here is much better
buttonState=digitalRead(buttonPin);
//Attach interrupt to button pin
attachInterrupt(0,countButtonPresses,FALLING);
}
void loop(){
int displayTime = 800;
Serial.println(buttonCounter);
switch(buttonCounter) {
case 1:
ExecuteIllumination(ledPins,displayTime,1);
break;
case 2:
ExecuteIllumination(ledPins,displayTime,2);
break;
}
}
void countButtonPresses()
{
buttonCounter++;
// modify this condition with number of patterns
if (buttonCounter>2)
buttonCounter=1;
}
void ExecuteIllumination(int ledPins[6],int DelayTime,int increments)
{
for(int i = 0; i <=5; i += increments){
digitalWrite(ledPins[i], HIGH);
delay(DelayTime);
digitalWrite(ledPins[i], LOW);
}
}
You may want to pay attention to bouncing effect of button:
Button debouncing
I am making a persistence of vision display using an ATTiny85, programmed with Arduino using the arduino-tiny core.
It consists of a stick with 16 LEDs on it which is spun round quickly to 'draw' a picture in the air. The display buffer is represented by an array, the next index of which is output every time the timer fires. It uses a hall sensor wired to INT0 to sense top dead centre, where it zeroes the array index.
It is an 8 bit processor and I have 16 LEDs connected to LED drivers, so I actually use two arrays for the display.
So the weird thing is that when I initialised the display with a cross pattern, it displayed a bunch of broken lines; so I zeroed it first in case memory had some random stuff in it. Now it doesn't display anything (even though I write the cross pattern to it straight after zeroing it). I have no idea what is happening, any ideas?
I had it previously outputting just the index value, and the picture it traces appears to look like counting binary, so I think the hardware is working.
Note that I'm not using digitalWrite because it disables interrupts which might throw the timing off.
Here is the code: (sorry it's quite a lot)
#define COLUMNCOUNT 180
const int datapin = 4;
const int clockpin = 0;
const int latchpin = 1;
const int rxpin = 3;
const int hallpin = 2;
volatile int columns0[COLUMNCOUNT];
volatile int columns1[COLUMNCOUNT];
volatile int counter = 0;
void setup()
{
pinMode(datapin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(latchpin, OUTPUT);
pinMode(rxpin, INPUT);
pinMode(hallpin, INPUT);
//make sure the arrays are all zeroed
for (int i = 0; i < COLUMNCOUNT; i++)
{
columns0[i] = 0;
columns1[i] = 0;
}
//make a cross pattern
columns0[0] = 255;
columns1[0] = 255;
columns0[45] = 255;
columns1[45] = 255;
columns0[90] = 255;
columns1[90] = 255;
columns0[135] = 255;
columns1[135] = 255;
//turn on the timer (prescale CK/16)
OCR1A = 255;
OCR1C = 255;
TCNT1 = 0;
TIMSK = _BV(OCIE1A);
TCCR1 = _BV(CTC1) | _BV(CS12) | _BV(CS10);
GIMSK = _BV(INT0);
sei();
}
void loop()
{
//nothing to do here
}
ISR(TIMER1_COMPA_vect)
{
if (counter < COLUMNCOUNT)
counter++;
outputWord(columns0[counter], columns1[counter]);
}
ISR(INT0_vect)
{
counter = 0;
}
void outputByte(int b)
{
int currentBit;
for (int i = 0; i < 8; i++)
{
currentBit = (b & 128) == 128;
PORTB = _BV(clockpin) | (currentBit ? _BV(datapin) : 0);
PORTB = PORTB ^ _BV(clockpin);
b <<= 1;
}
}
void outputWord(int hi, int lo)
{
outputByte(hi);
outputByte(lo);
PORTB = _BV(latchpin);
PORTB = 0;
}
Per C standard, global variables are initialised to 0. As such,
//make sure the arrays are all zeroed
for (int i = 0; i < COLUMNCOUNT; i++)
{
columns0[i] = 0;
columns1[i] = 0;
}
is not necessary. That action is taken care of automatically when the __do_clear_bss section is executed.
Also, as per C standard, the int type must be at least 16 bits wide. In AVR, the minimum is used. If you are using the free software toolchain, it contains inttypes.h which provides the functionality offered by stdint.h and some extra stuff. This was mentioned in another answer.
The statement:
PORTB = PORTB ^ _BV(clockpin);
can be rewritten as:
PINB = _BV(clockpin);
which compiles to only 1 instruction, as per Atmel's datasheets.
Macros provided by pgmspace.h can read from flash. Notice that you can not change the contents of flash as your program runs in most AVR chips.
Beware local/global variables and stack/heap collisions when handling this much data.
I've figured it out: it's an SRAM issue. The ATTiny85 only has 512 bytes of SRAM, so it can't hold all that data in memory at the same time. I'm investigating storing the data in flash memory using the PROGMEM directive instead.
I am also doing a Persistance of Vision (POV) with the Attiny44. (I am programming it via the Arduino as an ISP)
user Ben Jackson's tip for conserving SRAM memory was spot on: I originally was using "int"'s for my arrays and my Attiny would bug out if I tried to include additional arrays- However when I simply declared all of my arrays as "unsigned char"'s my code worked fabulously as it should!
Next time I will either order attiny45's or similar for more SRAM memory, and continue to use unsigned char's for my array declarations.
Thanks for the discussion guys!