Using the time function for a delay

Hello! I recently joined the community and have been scouring the forums and slowly been building an understanding of how the PB language works. The one part I am currently stuck on however is how the time function works. From what I understand, it creates a sawtooth wave that goes from 0 to 1 at a rate you can change. However, I don’t fully understand how I can use that to emulate the delay function from Arduino (if it even can!).

I am working on a hat that changes color when it detects a jolting movement (i.e. headbanging). My current idea is to modify the accelerometer example code from the tutorial, having the hat switch to a bright mode after detecting a large movement, and then reset back to the state it was at before. My initial thought was to place a small delay after changing the mode, so that the changing of modes looks like a bright blink. Maybe this is the wrong way to go about this (I am totally open to suggestions!), but how can I utilize the time function to implement a short delay?

Hey @shmimel!

I so remember struggling initially to wrap my head around how to do things in a world where I didn’t have sleep() or wait() to delay execution. But now I’m so glad I understand this alternate timing paradigm because it’s used in a lot of other places (gaming, task scheduling) and is more flexible and portable.

For your particular use case, which is around responding to an event and doing something for some amount of time right after, I’m going to suggest you use delta instead of time(). You’ll see the accelerometer example (scroll to “Example code: Shake to switch modes”) uses delta to do something similar, debouncing the shake motion. Debouncing means not letting it trigger multiple times as part of the same motion.

As a quick reminder:

// Stuff outside of either function - things to execute or declare one time
var hue

// Stuff to do once-per-frame, right before we recalculate all the colors for each pixel
// `delta` is the number of milliseconds that has passed since the last time beforeRender() ran.
export function beforeRender(delta) {
  // Excellent spot to work with time,
  //     compute things that don't vary based on pixel position,
  //     or read sensors or websocket API data.
}

export function render(index) {
  // Calculate things that vary based on the LEDs position in space
}

So with that reminder of where delta is used, here’s an example that will flash all the LEDs white for 1 second (or, 1000 milliseconds) after some motion is triggered.

var isBlinkingNow = 0
var currentBlinkDurationMs = 0

export function beforeRender(delta) {
  // Some imaginary accelerometer code here sets "isBlinkingNow" to 1 
  // if the desired motion is detected.
  if (!isBlinkingNow && accelerometerDetectionCode) isBlinkingNow = 1

  if (isBlinkingNow) {
    currentBlinkDurationMs = currentBlinkDurationMs + delta
    if (currentBlinkDurationMs > 1000) {
      currentBlinkDurationMs = 0
      isBlinkingNow = 0
    }
  }
}

export function render(index) {
  if (isBlinkingNow) {
    hsv(0, 0, 1) // all LEDs white
  } else {
    // Some other normal pattern code
    hsv(index / pixelCount, 1, .25)
  }
}

Folks sometimes refer to this technique as “accumulating delta”.

It’s possible to do something similar using just time(), but it’s a little less readable and can start to break down in scenarios where the waiting period is close to or greater than time()'s period or close to delta, so it’s not recommended. For the sake of completeness:

var isBlinkingNow = 0
var blinkStartRampValue = -1
var timerDuration = 1 // in seconds. 

export function beforeRender(delta) {
  // time(10 / 65.535) ramps from 0 to 1 every 10 seconds, so .2 is a second after .1.
  timeRampNow = time(10 / 65.535)

  // Some imaginary accelerometer code here sets "isBlinkingNow" to 1 
  // if the desired motion is detected.
  if (!isBlinkingNow && accelerometerDetectionCode) {
    isBlinkingNow = 1
    blinkStartRampValue = timeRampNow
  }

  if (isBlinkingNow) { // We're doing something for timerDuration
    if (
      timeRampNow > (blinkStartRampValue + timerDuration / 10) ||
        (   (timeRampNow < blinkStartRampValue) 
          && timeRampNow > (blinkStartRampValue + timerDuration / 10) % 1 
        )
      ) {
        isBlinkingNow = 0
    }
  }
}

export function render(index) {
  if (isBlinkingNow) {
    hsv(0, 0, 1) // all LEDs white
  } else {
    // Some other normal pattern code
    hsv(index / pixelCount, 1, .25)
  }
}

I don’t find that as easy to read since you need to detect time()'s potential wraparound.

5 Likes

Wow that was such and excellent and detailed response! I think I understand what I need to do and how to execute it. Thank you so much for your help!

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.