I am attempting to follow this tutorial to light up a dark staircase with motion-activated LED strips. If my understanding of the tutorial is correct, essentially the Arduino's job is to power or depower transistors at the appropriate time to prohibit/allow enough voltage over the LED strips on the stairs.
I have assembled a few light strips and soldered the microcontroller to the PCB with the appropriate components and was ready to test the device. The author of the guide helpfully created this video demonstrating the expected behavior of the device on start-up: the lights should blink as shown and then turn off, awaiting activation from a motion sensor. However when I try to replicate this my lights simply turn on and never turn off, fade, or blink.
Consulting the FAQ on the guide, I tried reorienting my transistors, thinking I perhaps had them backward. This had no effect.
I then tried to verify that my microprocessor was working and programmed correctly. I modified the code in the setup section, adding the following commands:
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
for(int i=0; i<10; i++){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(2000);
}
The intention was to make the built-in LED on the board blink on and off a few times during startup. While the board was connected to the computer, it indeed blinked on and off after I hit upload. When it's attached to the power supply from the PCB it does not blink (though the LED lights for the stairs turn on as I have described).
It seems to me that the microprocessor is simply not getting power or is not connected properly to the PCB. However I have done my best to verify all the connections and cannot find any problem. This is my first project of this type and am not sure what else to try. Any guidance with troubleshooting would be very much appreciated.
I apologize for the long post but I wanted to be sure to include all the necessary information. Thank you for reading!
Below are images of the PCB and schematics from the guide, which hopefully correspond to how I've actually connected the components.
Here is the code from the guide plus my blinking loop. The original is available at the author's github.
##Shift_Stairs.ino
#include <math.h>
#include <SPI.h>
//#include <MemoryFree.h>
//#include "expoDutyCycles.h"
//Data pin is MOSI (atmega168/328: pin 11. Mega: 51)
//Clock pin is SCK (atmega168/328: pin 13. Mega: 52)
const int ShiftPWM_latchPin=10;
const bool ShiftPWM_invertOutputs = 0; // if invertOutputs is 1, outputs will be active low. Usefull for common anode RGB led's.
#include <ShiftPWM.h> // include ShiftPWM.h after setting the pins!
const int SWITCH_PIN = A0;
const int PHOTORESISTOR_PIN = A2;
const int MOTION_SENSOR_TOP_PIN = 2;
const int MOTION_SENSOR_BOTTOM_PIN = 3;
const unsigned char maxBrightness = 255;
const unsigned char pwmFrequency = 75;
const int numRegisters = 2;
const int NUMLEDs = 9;
const int MOTION_SENSOR_WARMUP_TIME = 10;
const int ON_TIME = 10000; /* The duration between turn on and turn off. */
const int LIGHT_THRESHOLD = 300; /* Anything below this sensor value will enable lights */
/* These are used to detect rising edges in the absence of interrupts.
Using interrupts with ShiftPWM crashes the program. */
unsigned char lastReadTopPin = LOW;
unsigned char lastReadBotPin = LOW;
volatile unsigned char topActivated = false;
volatile unsigned char bottomActivated = false;
unsigned long lastMotionTime = 0;
const char BOTTOM_TO_TOP = 1;
const char TOP_TO_BOTTOM = 2;
/* For sake of the animation, stores the direction of propegation.
Set when animation is active, cleared when animation is done. */
char directionTriggered = 0;
const unsigned long BRIGHTNESS_SM_PERIOD = 2000; /* in μs */
unsigned long lastBrightnessSM = 0;
/* LED 0 is on the top of stairs */
unsigned char brightnesses[NUMLEDs] = {0};
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
for(int i=0; i<10; i++){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(2000);
}
pinMode(ShiftPWM_latchPin, OUTPUT);
SPI.setBitOrder(LSBFIRST);
// SPI_CLOCK_DIV2 is only a tiny bit faster in sending out the last byte.
// SPI transfer and calculations overlap for the other bytes.
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.begin();
Serial.begin(9600);
/* Turn on pullup resistor for switch */
digitalWrite(SWITCH_PIN, HIGH);
ShiftPWM.SetAmountOfRegisters(numRegisters);
ShiftPWM.Start(pwmFrequency,maxBrightness);
// Print information about the interrupt frequency, duration and load on your program
ShiftPWM.SetAll(0);
ShiftPWM.PrintInterruptLoad();
// Fade in all outputs
for(int j=0;j<maxBrightness;j++){
ShiftPWM.SetAll(j);
delay(3);
}
// Fade out all outputs
for(int j=maxBrightness;j>=0;j--){
ShiftPWM.SetAll(j);
delay(3);
}
}
void loop()
{
/* Detect rising edge with polling. Interrupts crash the program. */
unsigned char pinRead = digitalRead(MOTION_SENSOR_TOP_PIN);
if(pinRead == HIGH && lastReadTopPin == LOW){
topActivated = true;
}
lastReadTopPin = pinRead;
/* Detect rising edge with polling. Interrupts crash the program. */
pinRead = digitalRead(MOTION_SENSOR_BOTTOM_PIN);
if(pinRead == HIGH && lastReadBotPin == LOW){
bottomActivated = true;
}
lastReadBotPin = pinRead;
/* Resets flags */
if(topActivated){
if(directionTriggered == 0){
directionTriggered = TOP_TO_BOTTOM;
}
lastMotionTime = millis();
topActivated = false;
}
if(bottomActivated){
if(directionTriggered == 0){
directionTriggered = BOTTOM_TO_TOP;
}
lastMotionTime = millis();
bottomActivated = false;
}
/* State machine */
if(micros() - lastBrightnessSM > BRIGHTNESS_SM_PERIOD){
brightnessSM();
lastBrightnessSM = micros();
}
}
/**
Returns true if switch is in "1" position.
*/
boolean switchPressed(){
return !digitalRead(SWITCH_PIN);
}
###brightnessSM.ino
enum brightnessStates {
sFullyOn,
sOff,
sTurningOn,
sTurningOff,
sOverrideSwitch
};
int brightnessState = sOff;
void brightnessSM(){
/* Actions */
switch(brightnessState){
case sFullyOn:
break;
case sOff:
break;
case sTurningOn:{
/* Increase brightness of lights. This for loop goes from -NUMLEDs to 0 or
0 to NUMLEDs depending on the direction of propegration. */
char startLight = -1 * (directionTriggered - 1) * (NUMLEDs - 1);
char endLight = -1 * (directionTriggered - 2) * (NUMLEDs - 1);
for(char l = startLight; l <= endLight; l++){
/* Turn on the next LED only if the ones before it
are on*/
if(brightnesses[abs(l)] != maxBrightness){
brightnesses[abs(l)] += 5;
ShiftPWM.SetOne(abs(l), brightnesses[abs(l)]);
break;
}
/* Turn on the next LED only if the one before it is partially on */
// if(brightnesses[abs(l)] != maxBrightness){
// if(l == startLight || (brightnesses[abs(l - 1)] > maxBrightness*3/10)){
// brightnesses[abs(l)] += 1;
// ShiftPWM.SetOne(abs(l), expoDutyCycles[brightnesses[abs(l)]]);
// }
// }
}
break;
}
case sTurningOff:{
/* Decrease brightness of lights. This for loop goes from -NUMLEDs to 0 or
0 to NUMLEDs depending on the direction of propegration. */
char startLight = -1 * (directionTriggered - 1) * (NUMLEDs - 1);
char endLight = -1 * (directionTriggered - 2) * (NUMLEDs - 1);
for(char l = startLight; l <= endLight; l++){
/* Turn on the next LED only if the ones before it
are on*/
if(brightnesses[abs(l)] != 0){
brightnesses[abs(l)] -= 5;
ShiftPWM.SetOne(abs(l), brightnesses[abs(l)]);
break;
}
/* Turn on the next LED only if the one before it is partially on */
// if(brightnesses[abs(l)] != 0){
// if(l == startLight || (brightnesses[abs(l - 1)] < maxBrightness*9/10)){
// brightnesses[abs(l)] -= 1;
// ShiftPWM.SetOne(abs(l), expoDutyCycles[brightnesses[abs(l)]]);
// }
// }
}
break;
}
case sOverrideSwitch:
break;
}
/* Transitions */
switch(brightnessState){
case sFullyOn:
if(millis() - lastMotionTime > ON_TIME){
brightnessState = sTurningOff;
}
if(switchPressed()){
transitionToOverrideSwitch();
}
break;
case sOff:
if(directionTriggered != 0){
if(analogRead(PHOTORESISTOR_PIN) < LIGHT_THRESHOLD){
brightnessState = sTurningOn;
}
else {
directionTriggered = 0;
}
}
if(switchPressed()){
transitionToOverrideSwitch();
}
break;
case sTurningOn:{
/* If all the lights are on then proceed */
unsigned char allOn = true;
for(unsigned char l = 0; l < NUMLEDs; l++){
if(brightnesses[l] != maxBrightness){
allOn = false;
break;
}
}
if(allOn){
brightnessState = sFullyOn;
}
if(switchPressed()){
transitionToOverrideSwitch();
}
break;
}
case sTurningOff:{
/* If all the lights are off then proceed */
unsigned char allOff = true;
for(unsigned char l = 0; l < NUMLEDs; l++){
if(brightnesses[l] != 0){
allOff = false;
break;
}
}
if(allOff){
directionTriggered = 0;
brightnessState = sOff;
}
if(switchPressed()){
transitionToOverrideSwitch();
}
break;
}
case sOverrideSwitch:
if(switchPressed() == false){
/* Switch all LEDs off */
for(char l = 0; l < NUMLEDs; l++){
ShiftPWM.SetOne(l, 0);
brightnesses[l] = 0;
}
directionTriggered = 0;
brightnessState = sOff;
}
break;
}
}
void transitionToOverrideSwitch(){
brightnessState = sOverrideSwitch;
/* Switch all LEDs on */
for(unsigned char l = 0; l < NUMLEDs; l++){
ShiftPWM.SetOne(l, maxBrightness);
brightnesses[l] = maxBrightness;
}
}