5

I'm new to Arduino and I have two devices that I'm trying to control:

  1. A RGB LED light strip that can change colors
  2. A light sensor that can detect the ambient light level

What I'd like to do is have the LED strip continuously cycle through a rainbow of colors, and the light sensor will adjust the brightness of the LED strip based on the ambient light level. Here are the two pieces of code by themselves, which I've tested in isolation and confirmed that they work properly:

LED strip:

j = (j + 1) % 256; // current iteration of the light cycle

// set the strip color
for(int i=0; i< strip.numPixels(); i++) {
  strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}

// display the new colors and wait 20ms before the next cycle
strip.show();
delay(20);

Light sensor:

sensors_event_t event;
tsl.getEvent(&event); // get a light measurement
strip.setBrightness(event.light); // set the brightness of the LED strip

The problem is that the light sensor takes anywhere from 100ms to 600ms to get a reading, and tsl.getEvent(&event) is a blocking call, so the end result is that the LED strip updates way too slowly. I would need to run the two side by side so that the light sensor read does not block the LED strip update. Any idea how I can accomplish this?

  • what library is "tsl" coming from? – BrettAM Sep 28 '14 at 22:46
  • 1
    100 to 600ms is excessive. Is it just for a LDR connected to an analog input? – Ignacio Vazquez-Abrams Sep 28 '14 at 22:51
  • 1
    @BrettM I think he is using the TSL2561 I2C light sensor. – Gerben Sep 29 '14 at 12:56
6

You can make the sensor return data faster by lowering it's accuracy.

tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);      /* fast but low resolution */

You can also modify the library to not power-down the sensor after each reading. Just open TSL2561.cpp, find the getFullLuminosity function, and comment out the line reading disable(); and the 12 lines reading switch (_integration){ ... }

Another solution is, to have the pixels update every 20ms using a timer. You can have the timer, interrupt the main loop every 20ms and update the pixel values.

(I can't really help you with timers, as I myself set the registers for my timers, and ISRs directly, which isn't for beginners. But there are probably libraries for it, to make it more user friendly.)

  • playground.arduino.cc/code/timer1 this library should help make timers easier – BrettAM Sep 29 '14 at 15:50
  • See my edit above for negating the need for any delay, by leaving the tsl2561 on, all the time. – Gerben Sep 29 '14 at 18:50
3

The arduino is not a multithreaded or multi-tasking device. You have to write your different functions to "play nice" with each other. Blocking code is death for this sort of multitasking. You might need to rewrite your code that reads the light sensor to be non-blocking.

If you can get that code to be non blocking then Visual Micro's post is the way to go.

1

Setup a 'state server' in your loop(). Put simply this means keeping track of the next command for each task, then doing one thing in each task per loop().

If one task needs to run at a faster speed than another task you can use a timer in your loop to decide what to do next.

If a task needs a pause then save the "Last Run" millis() to a global variable and ignore the task until the correct amount of time has passed. Do not use a delay(), always let the loop() run.

The idea is that the loop() runs as often as possible but you do one thing each time it runs.

It takes a while to get the hang of this way of working but it leaves the Arduino processor free to process all jobs evenly.

int task1Status=0;
int task2Status=0;

void loop()
{
  doOneThingOfTask1();
  doOneThingOfTask2();
}

//10 things to light the leds, but only do one
void doOneThingOfTask1()
{
  if (task1Status==10)
    task1Status=0;

  switch (task1Status)
  {
    case 0:
       //start doing a new process

    case 1:
       //do something

    case 9:
       //do the last thing

  }
}
  • 1
    I was going to post something similar, then I noticed that the OP said that the code for taking light measurements is blocking code. – Duncan C Sep 28 '14 at 23:41
  • @Duncan C Oh! Good point. I will leave the answer because I think it describes a way of working that avoids the same issue elsewhere in code. Also it might be that by examining the library call to getEvent() the same solution can be applied to reduce the delay. Obviously this means changing the library code but one can copy the library into the sketch folder and hack it as local source. a Lib change would certainly help but local source and a little hacking might produce a solution. Thanks :) – Visual Micro Sep 29 '14 at 12:17
1

Your first, biggest problem is

the light sensor takes anywhere from 100ms to 600ms to get a reading, and tsl.getEvent(&event) is a blocking call,

You need non-blocking access to the light sensor. Look at the library code to see what it is doing when it blocks. Chances are it waits for the sensor to set a 'done' bit. If so, you'll need to modify the library, either by

  1. adding a boolean .isDone() function that tests the done bit, and don't let your main program call for a reading until isDone() == TRUE; or
  2. making .getEvent() a non-blocking such that it immediately returns an obviously invalid reading (such as -1) instead of blocking if the sensor isn't ready yet.

That leaves your main program free to service whatever needs doing and is ready to do without holding up everything for the slow sensor.

0

There are libraries that allow multitasking, for instance my library arduOS.

This should work:

#include <arduos16.h>
#include <arduos_core.h>
#include <roundscheduler.h>

SYS_enable_preemptive; // do NOT forget this!!!
void setup()
{
    auto sched = new SYS::RoundScheduler(2);
    sched->add(&loop1); // those 2 are functions.
    sched->add(&loop2);
    SYS::start();
}

But keep in mind that actual multitasking can be rather tricky, with very obscure bugs. (but you can find them).

  • This OS looks easier to use than your average arduino OS – EternityForest Oct 24 '14 at 14:42
0

1.Use two arduino's and have them talk to each other via serial or i2c. Probably the simplest option (but not the cheapest - though you can get clone arduinos from US$4 ).

2.Find a light sensor that updates faster. Tried an ldr(light dependent resistor)/photocell ? Then could use analog read.

Could also switch from using arduino to c, but thats a lot more work and you are still going to hit similar problems.

  • Switching to C would do nothing, really, for this issue. – Anonymous Penguin Jan 1 '15 at 23:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.