0

I am an amateur with Arduino and coding and I am learning in my lab class. My project consists of using a photoresistor to measure the brightness of a white LED. I have the following circuit built.

enter image description here

I am having problems with the code because I need the serial monitor to print every 1/10 of a second for 10 seconds. My professor started talking about the micros() function but never showed an example. I have an idea as to how I can utilize this function to control how long the Serial Monitor prints for but I have been unsuccessful. I know that setting a delay is good enough for reading every 1/10 of a second but how can I make the serial monitor print values for only 10 seconds?

I believe it should be a total of 100 readings.

This is my current code:

unsigned long start, finished, elapsed, total;
int analogInPin = A0;
int LEDreading;           // analog reading
int LEDpin = 9;           // LED on pin 9

void setup() {
  Serial.begin(9600);
}

void loop() {
  analogWrite(LEDpin,255);  // LED brightness
  start = millis();
  LEDreading = analogRead(analogInPin);
  Serial.println(LEDreading);
  delay(100);
  finished = millis();
  elapsed = finished - start;
  total = elapsed*100;

  if (total >= 10000){
    Serial.end();
}
2

Using micros() is very much like boiling an egg.

When you want your egg to be cooked just right you know that it needs to be in the boiling water for a certain amount of time. There's three ways you can achieve that:

  1. Use an egg timer to alert you when the time has passed,
  2. Sit and stare at the boiling pot counting the seconds as they slowly tick by (booooooring..... ...zzzZZZzzz...)
  3. Remember what time you put the egg in the water and glance at the clock every so often to see how much time has passed.

Using millis() is like option 3. It tells you how many milliseconds have passed since the board was turned on (or since the last millis() rollover at roughly 49 days). So when you put the egg in the water you record the current time. Then you periodically compare the recorded time to the current time (a simple subtraction will suffice) to see if enough time has passed. When it has you remove the egg and chow down.

So to put that more in a coding context - when you start some operation, or want to measure a period of time from a point, you just save the time in a variable:

start = millis();

(start must be uint32_t, aka unsigned long). To find if enough time has passed you just subtract that saved time from the current time and it gives you the elapsed time, which you can compare to your required elapsed time:

if (millis() - start >= 100) { 
}

1/10 of a second is 100ms, so if you hit a difference of 100ms (or more) then your time has arrived and you can do something.

If you want it to do it again after that then just set the "start" time to the current time again.

For instance, for your requirements:

void loop() {
    // Record the start time - the static means it
    // only happens once the first time loop() is run and
    // it remembers the value between iterations
    static uint32_t start = millis();

    // Somewhere to store the number of values we
    // have given - again, static so it remembers across
    // iterations of loop()
    static uint8_t count = 0;

    if (count < 100) { // No more than 100 counts
        if (millis() - start >= 100) { // Tick
            start = millis(); // Do it again in 100ms time

            // Add whatever you need for your readings here

            count++; // Add one to the count
        }
    }
}
1

Using delay(100) will cause the sketch to wait at that line for 100 milliseconds. That is not (quite) the same as DOING something every 100 milliseconds. The difference is that other things take time too, so the time will be 100 milliseconds plus how ever long everything else takes.

The solution used by @Majenko still has that problem in a small way, because a new millis() value is used to set the new value of start. In most situations, it will work fine, but it is possible that the either or both of the millis() results were more than 100 after the previous value of start. That would happen if code that replaces the comment took more than 100ms some time. The fix for that is very simple. Just change start = millis() to start += 100. That will attempt to speed up the following loop, if it was getting behind.

As a side note, it would be good to change the 100 millisecond references to a defined name. Either a #define or a const. Since the millis() function returns an "unsigned long" value, I would use the same data type for a related const value. That just means that the compiler will not have to (invisibly) convert between different types when doing the math operations.

#define REPORT_CYCLE 100
const unsigned long REPORT_CYCLE = 100; // milliseconds between measurement and reporting cycles

If you are a beginner, you may not have seen static variables yet. If you know about them, fine, but if not, they can be replaced by global scope variables.

The @Majenko code uses a fixed count of 100 iterations to do the measurement and reporting. That works fine as long as neither the for 10 seconds or every 1/10 second changes. If either of those change, the count limit would also need to be recalculated and changed. Instead, I would use another defined name for how long to do measurement and reporting cycles, and let the compiler handle how many times that actually is.

Doing an analogWrite() sets the value (brightness here), which will stay constant until changed again. Since your program only sets a single value, it only needs to be done once. Move that statement to setup().

There is no need to do Serial.end() when the reporting is done. That disables serial communication, but is unneeded, if no more Serial.println() commands are executed anyway.

To avoid calling millis() multiple times in the same 'pass' (in each if test), I like to call it once, and save the result.

Taking the @Majenko code, plus your starting sample, and described changes gets

// Intervals in milliseconds
#define REPORT_CYCLE 100
#define REPORT_INTERVAL 10000
unsigned long nextStart, endTime;
int analogInPin = A0;
int LEDreading;           // analog reading
int LEDpin = 9;           // LED on pin 9

void setup() {
  Serial.begin(9600);
  analogWrite(LEDpin,255);  // LED brightness
  nextStart = millis(); // Time for "right now"
  endTime = nextStart + REPORT_INTERVAL;
}

void loop() {
  unsigned long now = millis();
  if (now < endTime) { // still need to do more reporting
    if (now >= nextStart) { // time to get and report another reading
      nextStart += REPORT_CYCLE; // When the next reading should be done

      LEDreading = analogRead(analogInPin);
      Serial.println(LEDreading);
    }
  }
  // do other things, both while still reporting, and after reporting is done
}

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.