Освещение, активируемое движением

arduino-pro-mini led-strip

Я пытаюсь следовать этому руководству, чтобы осветить темный лестница со светодиодными лентами, активируемыми движением. Если мое понимание руководства верное, по сути, работа Arduino заключается в том, чтобы включать или выключать транзисторы в нужное время, чтобы запретить/разрешить достаточное напряжение на светодиодных лентах на лестнице.

Я собрал несколько световых полосок и припаял микроконтроллер к плате с соответствующими компонентами и был готов протестировать устройство. Автор руководства любезно создал это видео, демонстрирующее ожидаемое поведение устройства при запуске: индикаторы должны мигать, как показано, а затем выключаться, ожидая активации от датчика движения. Однако, когда я пытаюсь воспроизвести это, мои огни просто включаются и никогда не выключаются, не исчезают и не мигают.

Консультируясь с часто задаваемыми вопросами в руководстве, я попытался переориентировать свои транзисторы, думая, что, возможно, я перевернул их. Это не имело никакого эффекта.

Затем я попытался убедиться, что мой микропроцессор работает и правильно запрограммирован. Я изменил код в разделе настройки, добавив следующие команды:

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
for(int i=0; i<10; i++){
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    delay(2000);
}

Намерение состояло в том, чтобы встроенный светодиод на плате несколько раз мигал во время запуска. Пока плата была подключена к компьютеру, она действительно замигала после того, как я нажал кнопку «Загрузить». При подключении к блоку питания от платы он не мигает (хотя светодиоды для лестницы включаются, как я описал).

Мне кажется, что микропроцессор просто не получает питание или неправильно подключен к плате. Однако я сделал все возможное, чтобы проверить все соединения и не могу найти никаких проблем. Это мой первый проект такого типа, и я не знаю, что еще попробовать. Будем очень признательны за любые рекомендации по устранению неполадок.

Прошу прощения за длинное сообщение, но я хотел обязательно включить всю необходимую информацию. Спасибо за прочтение!

Ниже приведены изображения печатной платы и схемы из руководства, которые, надеюсь, соответствуют тому, как я на самом деле соединил компоненты.

Схема печатной платы

Схема печатной платы

Мое устройство

Вот код из руководства плюс мой мигающий цикл. Оригинал доступен на гитхабе автора.

##Shift_Stairs.ino

#include <math.h>
#include <SPI.h>
//#включить <MemoryFree.h>
//#include "expoDutyCycles.h"

// Контакт данных - MOSI (atmega168/328: контакт 11. Mega: 51)
// Тактовый контакт — SCK (atmega168/328: контакт 13. Mega: 52)
const int ShiftPWM_latchPin=10;
const bool ShiftPWM_invertOutputs = 0; // если invertOutputs равно 1, выходы будут активны на низком уровне. Полезно для светодиодов RGB с общим анодом.

#include <ShiftPWM.h>   // включаем ShiftPWM.h после установки пинов!

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 лишь немного быстрее отправляет последний байт.
    // Передача SPI и вычисления перекрываются для других байтов.
    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);  
    // Выводим информацию о частоте прерываний, продолжительности и нагрузке на вашу программу
    ShiftPWM.SetAll(0);
    ShiftPWM.PrintInterruptLoad();
    // Затухание всех выходных данных
    for(int j=0;j<maxBrightness;j++){
        ShiftPWM.SetAll(j);  
        delay(3);
    }
    // Исключаем все выходные данные
    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)){
// яркость[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 */
// если(яркости[abs(l)] != 0){
// if(l == startLight || (brightnesses[abs(l - 1)] < maxBrightness*9/10)){
// яркость[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;
    }        
}

, 👍2

Обсуждение

Проверьте ориентацию Arduino. Это вверх ногами? Проверьте также напряжение между контактами, помеченными GND и VCC., @Edgar Bonet

Спасибо за ответ. Я читаю 12 В на контактах, что кажется хорошим. Я почти уверен, что ориентация правильная, но я еще раз проверю., @Eidan Jacob

Судя по схемам, похоже, что в (поддельной) версии платы, которую я использовал, разъем FTDI ориентирован наоборот, но остальные контакты совпадают. У меня есть несколько запасных частей, так что я могу попробовать перевернуть, если ничего не работает., @Eidan Jacob

Спасибо, изображения добавлены, код будет следовать. Я был бы рад добавить что-нибудь еще, что вы считаете полезным., @Eidan Jacob

Где именно вы измеряли 12В? Вы измеряли между Vcc и землей Arduino? 12В было бы плохо., @chrisl

что произойдет, если вы запустите минимальный скетч, например мигание встроенным светодиодом?, @jsotola

схема не имеет сброса ... выходы всегда включены ... это означает, что выход может быть в любом состоянии при включении питания ... можно начать с простого скетча, чтобы побитить входные сигналы ... установить контакты 10, 11, 13 LOW, переключить контакт 13 LOW-HIGH-LOW шестнадцать раз, чтобы сдвинуть все нули, переключить контакт 10 LOW-HIGH-LOW для передачи сдвинутых данных на выходы... задержка на одну секунду... установить контакт 11 HIGH и повторите действия с контактом 13 и контактом 10 ... это должно выключить все светодиоды, а затем снова включить их ... контакт 11 - данные, контакт 13 - часы последовательного переключения, контакт 10 - выходная защелка Часы, @jsotola

Я могу заставить встроенный светодиод мигать, когда Arduino подключен к компьютеру и я загружаю скетч. На Arduino ничего не происходит, когда я подключаю блок питания к печатной плате. Код был проверен другими пользователями, которые успешно следовали руководству, поэтому я был бы очень удивлен, если бы это было источником проблемы. Когда у меня будет возможность, я попробую вашу идею., @Eidan Jacob

ваша схема похожа на https://wokwi.com/projects/301213976182653448, @jsotola

Твоя пайка выглядит ужасно. Недостаточно тепла, нет флюса, не прогрета деталь перед подачей припоя... что-то. Прежде чем беспокоиться о многом другом, я бы выяснил, что с этим не так, потому что до тех пор это может просто свести с ума любого, кто попытается помочь вам., @timemage


1 ответ


Лучший ответ:

0

Изображение печатной платы (PCB), размещенное в предыдущем вопросе, содержит множество примеров того, что обычно называют Холодная пайка. Эти типы соединений ненадежны и иногда не обеспечивают электрического соединения. И зачастую их невозможно визуально проверить. первые 2 минуты этого видео демонстрируют, как можно комбинировать нагрев и флюс для исправления соединений холодной пайки, включающих печатную плату и сквозной штифт.

(Я могу отредактировать/изменить следующее, поскольку мы сужаем решение этой проблемы. Цель состоит в том, чтобы сделать хороший вопрос и ответ для обмена стеками.)

Симптом:

  • При подключении к ПК проект ведет себя так, как ожидалось. Это предполагает, что кабель к ПК (вероятно, от USB/CDC к последовательному порту) содержит заземление, вход, выход и питание. Ожидаемое поведение — включение и выключение светодиодов.
  • Проект не работает при отключении от ПК и питается от внешнего блока питания. Только горят светодиоды.

Рассмотрите возможность проверки питания процессора или, что еще лучше, процессора. При описанной в вопросе проблеме может быть обрыв пути питания от внешнего блока питания к процессору. Проверьте все паяные соединения, связанные с подключением входа питания Arduino к печатной плате. Вероятно, процессор (Arduino) был включен во время программирования через контакты заземления и питания кабеля последовательного порта. После того, как этот кабель был удален, процессор мог не получать питание и, возможно, не работал, в то время как плата питалась только от внешнего источника.

,