Non-blocking theater style chase animation for NeoPixels - c++

I am trying to adapt this version of the Adafruit NeoPixel theater chase example to be non-blocking by not using the delay() function and instead of using the millis() function to create a counter, however, I am having no luck as the NeoPixels just light up constantly. Am I missing something? I decided to place the if statement at the point where the nested for loop turns off every 3rd pixel in the strand thinking this would be the spot to put it since in the old code the delay() was called previous to this step.
Here is this version I made which doesn't work:
//Theatre-style crawling lights.
void theaterChase(uint32_t c, const long wait) {
unsigned long currentMillis = millis();
for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
for (int q = 0; q < 3; q++) {
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
if (currentMillis - previousMillis >= wait) {
previousMillis = currentMillis;
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
}
}
and here is the old version:
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}

The for loops wrapped around the delay are what does the blocking.
You have to disassemble the nested for loops that are wrapped around delay() into their constituent parts.
You can turn the for(j...){} loop into the if( currentMillis -last > wait){} conditional and rely on the outer loop() to call this function frequently.
You make q save state with a static and do the iteration arithmetic yourself
Untested code:
//Theatre-style crawling lights.
void theaterChase(uint32_t c, const long wait) {
static unsigned long last;
static int q;
unsigned long currentMillis = millis();
if( currentMillis -last > wait){
last = currentMillis;
if(q >= 3) q = 0;
if(q < 3){
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
// setup to turn them back off during the next iteration
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
q++;
}// if(q ...
} // if( currentMillis...
}

Related

I'm coding Arduino, but I'm confused about combining 2 sensors and 1 servo with a push button

I'm coding Arduino, but I'm confused about combining 2 sensors and 1 servo with a push button. I hope someone can help me.
I have made one by one the sensor coding and it works, but I want to combine them into one program.
// code void loop water temperatur sensor
void loop(void`{
sensors.requestTemperatures();
Celcius = sensors.getTempCByIndex(0);
Serial.print(Celcius);
Serial.println(" C ");
delay(1000);
}
// this code push button with servo
// code void servo with push button
void loop() {
if (digitalRead(pushButtonPin) == LOW) {
buttonPushed = 1;
Serial.println("Servo ON");
delay(1000);
}
if (buttonPushed) {
// change the angle for next time through the loop:
angle = angle + angleStep;
// reverse the direction of the moving at the ends of the angle:
if (angle >= maxAngle) {
angleStep = -angleStep;
if (type == 1) {
buttonPushed =0;
}
}
if (angle <= minAngle) {
angleStep = -angleStep;
if (type == 2) {
buttonPushed =0;
}
}
myservo.write(angle); // move the servo to desired angle
delay(100); // waits for the servo to get there
}
}
// Ph Sensor code
void loop(void) {
static unsigned long samplingTime = millis();
static unsigned long printTime = millis();
static float pHValue, voltage;
if (millis() - samplingTime > samplingInterval) {
pHArray[pHArrayIndex++] = analogRead(SensorPin);
if (pHArrayIndex==ArrayLenth)
pHArrayIndex=0;
voltage = avergearray(pHArray, ArrayLenth) * 5.0 / 1024;
pHValue = 3 * voltage + Offset;
samplingTime=millis();
}
if (millis() - printTime > printInterval) { //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
Serial.print("Voltage:");
Serial.print(voltage, 2);
Serial.print(" pH value: ");
Serial.println(pHValue, 2);
digitalWrite(LED, digitalRead(LED) ^ 1);
printTime = millis();
}
}
double avergearray(int* arr, int number){
int i;
int max, min;
double avg;
long amount = 0;
if (number <= 0) {
Serial.println("Error number for the array to avraging!/n");
return 0;
}
if (number<5) { //less than 5, calculated directly statistics
for (i=0; i<number; i++) {
amount += arr[i];
}
avg = amount / number;
return avg;
} else {
if (arr[0] < arr[1]) {
min = arr[0];
max = arr[1];
} else {
min = arr[1];
max = arr[0];
}
for (i=2; i<number; i++) {
if (arr[i] < min) {
amount += min; //arr<min
min = arr[i];
} else {
if (arr[i] > max) {
amount += max; //arr>max
max = arr[i];
} else {
amount += arr[i]; //min<=arr<=max
}
} //if
} //for
avg = (double)amount / (number - 2);
} //if
return avg;
}
Your "Ph Sensor code" demonstrates how to do 2 things at different time intervals in one loop.
void loop() {
if (/* time is right to do thing 1 */) {
// do thing 1
}
if (/* time is right to do thing 2 */) {
// do thing 2
}
}
This is called a state machine. You can extend this logic to do 4 or more things in one loop.
Obviously you can't use delay as it blocks the entire loop, preventing other work from continuing. So you need to convert the first two loops into the structure similar to the one above. Then you will be able to merge all the loops into a single one.

Generating threads in for loop

I am trying to separate my collision detection into separate theads to improve the efficiency. The code I have now is:
/*CHECK COLLISION HERE*/
std::thread threads[6];
Vector2 birdPointsArr[6];
Vector2* birdPoints = getBirdPoints(birdPointsArr);
//check each pipe
for (int i = 0; i < 6; i++)
{
//only check pipes with viable x coords
if (pipes[i]->getX() < birdPoints[2].x + 20 && pipes[i]->getX() + pipes[i]->getW() > birdPoints[4].x - 20)
{
//check both upper and lower pipe
for (int j = 0; j < 2; j++)
{
Vector2 pipePointsArr[4];
Vector2* pipePoints = getPipePoints(pipePointsArr, i, j);
//only check pipes with viable y coords
if (j == 0 && birdPoints[0].y > pipePoints[2].y + 20)
{
continue;
}
if (j == 1 && birdPoints[3].y < pipePoints[0].y - 20)
{
continue;
}
threads[i] = std::thread([&]() {
//if x and y coords are viable, check collision
if (collisionDetection(birdPoints, pipePoints)) {
dead = true;
}
});
}
}
}
for (int i = 0; i < 6; i++)
{
if(threads[i].joinable())
threads[i].join();
}
My concern is that I am creating and destroying several threads each frame. I am concerned that all this creation and destruction makes my code slower than if I were to leave it synchronous; however, I don't know how to really measure that. Would it be faster if I just reverted it to serial form?

function that set pin high for defined number of times and time

I have this problem where I try to set an output pin high for a set time and times.
I do the call with hapticFeedback(1000, 2, 1);
the variables are defined as
unsigned long hapticPreviousMillis = 0;
int hapticState = LOW;
int oneshotHaptic = 0;
here is the function. For some reason I only get the pin set HIGH and not the blinks and LOW
void hapticFeedback(int activeLength, int repeats, int oneshotHaptic) {
if (oneshotHaptic == 1) {
for (int x = 0; x <= repeats; x++) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (hapticState == LOW) {
hapticState = HIGH;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
}
oneshotHaptic = 0;
}
So I figured it out and if anyone else is looking for this here is what I came up with. It might not be the smoothest of code but it does what I intended it to do
in the loop I have
if (setOneshotHaptic == 1) {
hapticFeedback(activeLength);
}
and the haptic function look like this
void hapticFeedback(int activeLength) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (x == repeats) {
setOneshotHaptic = false;
hapticState = HIGH;
x = 0;
}
if (hapticState == LOW) {
hapticState = HIGH;
x++;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
whenever i like to have haptic feedback i can define the following vars
setOneshotHaptic = true;
repeats = 3;
activeLength = 1000;
When the number of repeats has been reached I lay down the oneshot, force the output to high for it to be low by the routine and finally reset my repeat counter.
There might be nicer ways to do this. However I couldn't find them and this works for me....

How can I define a game over in snake game using SFML?

so recently I have been wanting to make a snake game in C++ using SFML in Microsoft Visual studio 2015 and I made one and I am actually pretty satisfied with my work but there is a problem, that I forgot to make a game over for it and it seems like I couldn't make it work and it really had me on edge. So I thought I could use stack overflow's help. I would really appreciate it if you guys would let me know how to make it work and please keep it simple obvious.
Here is my code:
// GraphicalLoopSnakeGame.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <ctime>
#include <SFML/Graphics.hpp>
using namespace sf;
int N = 30, M = 20;
int size = 16;
int w = size * N;
int h = size * M;
int dir, num = 4;
struct Snake {
int x, y;
} s[100];
struct Fruit {
int x, y;
} f;
void Tick() {
for(int i = num; i > 0; --i) {
s[i].x = s[i - 1].x;
s[i].y = s[i - 1].y;
}
if(dir == 0) s[0].y += 1;
if(dir == 1) s[0].x -= 1;
if(dir == 2) s[0].x += 1;
if(dir == 3) s[0].y -= 1;
if((s[0].x == f.x) && (s[0].y == f.y)) {
num++;
f.x = rand() % N;
f.y = rand() % M;
}
if(s[0].x > N) s[0].x = 0;
if(s[0].x < 0) s[0].x = N;
if(s[0].y > M) s[0].y = 0;
if(s[0].y < 0) s[0].y = M;
for(int i = 1; i < num; i++)
if(s[0].x == s[i].x && s[0].y == s[i].y) num = i;
}
int main() {
srand(time(0));
RenderWindow window(VideoMode(w, h), "Snake Game!");
Texture t1, t2, t3;
t1.loadFromFile("images/white.png");
t2.loadFromFile("images/red.png");
t3.loadFromFile("images/green.png");
Sprite sprite1(t1);
Sprite sprite2(t2);
Sprite sprite3(t3);
Clock clock;
float timer = 0, delay = 0.13;
f.x = 10;
f.y = 10;
while(window.isOpen()) {
float time = clock.getElapsedTime().asSeconds();
clock.restart();
timer += time;
Event e;
while(window.pollEvent(e)) {
if(e.type == Event::Closed) window.close();
}
if(Keyboard::isKeyPressed(Keyboard::Left)) dir = 1;
if(Keyboard::isKeyPressed(Keyboard::Right)) dir = 2;
if(Keyboard::isKeyPressed(Keyboard::Up)) dir = 3;
if(Keyboard::isKeyPressed(Keyboard::Down)) dir = 0;
if(Keyboard::isKeyPressed(Keyboard::W)) dir = 3;
if(Keyboard::isKeyPressed(Keyboard::D)) dir = 2;
if(Keyboard::isKeyPressed(Keyboard::A)) dir = 1;
if(Keyboard::isKeyPressed(Keyboard::S)) dir = 0;
if(timer > delay) {
timer = 0;
Tick();
}
////// draw ///////
window.clear();
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++) {
sprite1.setPosition(i * size, j * size);
window.draw(sprite1);
}
for(int i = 0; i < num; i++) {
sprite2.setPosition(s[i].x * size, s[i].y * size);
window.draw(sprite2);
}
sprite3.setPosition(f.x * size, f.y * size);
window.draw(sprite3);
window.display();
}
return 0;
}
In your Tick() function you can check whether the head bumps into anything after everything has moved in the given direction. If it does, let main() know about it somehow: for example, return a bool which expresses if the game is over. Let's say this bool is called over.
So, add if (over) { window.close(); } inside your while (window.isOpen()) loop (right after calling Tick()) to let main() reach return 0; and finish the program.
EDIT: Think about using std::deque for moving your snake using less code and time: you'd be able to just pop_back() the snake tile farthest from the head and push_front() the new tile where the head currently is (after a tick), simulating crawling one step forwards.
Anyway, after having moved your snake you can check each of its body tiles whether it has the same coordinates as its head. If it does, it means your snake crashed into its tail so the game is over.
// in Tick():
// ...other logic...
tiles.pop_back();
tiles.push_front(new_head_position);
for (/* each tile of your snake except its head */) {
if (tile.x == head.x && tile.y == head.y) {
return false; // game over
}
}
return true; // everything is fine

Function not looping properly for Arduino with NeoPixels

I'm animating/lighting a sign using an Arduino + NeoPixels for Halloween at my place of work. One of my two functions (a slow, spreading, red glow) works fine. The other - a slowing heartbeat - never slows down and seems stuck in an infinite loop.
Here's the full, relevant code:
void loop() {
// HeartBeat Test
for(int bpm=60;bpm >= 0;bpm=bpm-3) {
systolicUp(bpm);
systolicDown(bpm);
diastolicUp(bpm);
diastolicDown(bpm);
restBeat(bpm);
}
}
void systolicUp(int i) {
uint16_t beatSeconds, firstPulse, firstIncrement, j, k;
beatSeconds = 60000/i;
firstPulse = beatSeconds * 0.6;
firstIncrement = firstPulse/170;
for(j=0; j <= 255; j=j+3) {
uint32_t redShade = strip.Color(j, 0, 0);
for (k=0; k<strip.numPixels(); k++) {
strip.setPixelColor(k, redShade);
}
strip.show();
delay(firstIncrement);
}
}
void systolicDown(int i) {
uint16_t beatSeconds, firstPulse, firstIncrement, j, k;
beatSeconds = 60000/i;
firstPulse = beatSeconds * 0.6;
firstIncrement = firstPulse/170;
for(j=255; j >= 0; j=j-3) {
uint32_t redShade = strip.Color(j, 0, 0);
for (k=0; k<strip.numPixels(); k++) {
strip.setPixelColor(k, redShade);
}
strip.show();
delay(firstIncrement);
}
}
void diastolicUp(int i) {
uint16_t beatSeconds, secondPulse, secondIncrement, j, k;
beatSeconds = 60000/i;
secondPulse = beatSeconds * 0.3;
secondIncrement = secondPulse/170;
for(j=0; j <= 255; j=j+3) {
uint32_t redShade = strip.Color(j, 0, 0);
for (k=0; k<strip.numPixels(); k++) {
strip.setPixelColor(k, redShade);
}
strip.show();
delay(secondIncrement);
}
}
void diastolicDown(int i) {
beatSeconds = 60000/i;
uint16_t beatSeconds, secondPulse, secondIncrement, j, k;
secondPulse = beatSeconds * 0.3;
secondIncrement = secondPulse/170;
for(j=255; j >= 0; j=j-3) {
uint32_t redShade = strip.Color(j, 0, 0);
for (k=0; k<strip.numPixels(); k++) {
strip.setPixelColor(k, redShade);
}
strip.show();
delay(secondIncrement);
}
}
void restBeat(int i) {
uint16_t beatSeconds, rest, g;
beatSeconds = 60000/i;
rest = beatSeconds * 0.1;
for (g=0; g<strip.numPixels(); g++) {
strip.setPixelColor(g, 0, 0, 0);
}
strip.show();
delay(rest);
}
By the math, I should be handing over a certain number of beats-per-minute, the number of seconds-per-beat is calculated, the light pulses once over 60% of that, pulses again over 30%, and then is silent for 10%. This should happen 20 times, with each beat getting slower progressively until it stops completely.
Instead, I'm getting steady once-per-second blinking.
I'm certain it'll end up being something that I've totally overlooked, or missed in the NeoPixel documentation. But, as I'm either overlooking or have totally missed it, help would be appreciated. :)
void diastolicDown(int i) {
beatSeconds = 60000/i;
uint16_t beatSeconds, secondPulse, secondIncrement, j, k;
Here you're trying to assign a value to beatSeconds before its declaration.