hello i just got an ac dimmer breakout board in the mail and i have managed to put together a program with some information i found on the forums. the ultimate goal here is to base the brightness on the time of day. i have a reptile enclosure that i would like to use this dimmer to control the heat lamps brightness based on the time of day to make things a little more natural. whats the best approach to do this. the program im using the brightness setting is a value of 0-255. so far i have the dimmer working and it works better than i expected but now i need to find a way to sorta sync it to real time. so it will start out dim at lets say 6am at a brightness of 10 but by the time i get to 3-4 o'clock the brightness will be at 255 (max), then from 4 to 8pm the value begins to drop until about 8am when the bulb will finally go out completely. hopefully someone can make sense of this . here is the program i'm using now thanks in advanced!
int AC_pin = 3;//Pin to OptoTriac
byte dim = 0; //Initial brightness level from 0 to 255, change as you like!
const byte numChars = 50;
char receivedChars[numChars];
char tempChars[numChars];
char StringPt[numChars] = {0};
unsigned long integerPt = 0;
float floatFromPC = 0.0;
boolean newData = false;
void setup() {
Serial.begin(115200);
pinMode(AC_pin, OUTPUT);
attachInterrupt(0, light, FALLING);//When arduino Pin 2 is FALLING from HIGH to LOW, run light procedure!
Serial.println(F("Ready!"));
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
long rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0';
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void parseData() {
char * strtokIndx;
strtokIndx = strtok(tempChars, ",");
strcpy(StringPt, strtokIndx);
strtokIndx = strtok(NULL, ",");
integerPt = atol(strtokIndx);
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx);
}
void showParsedData() {
if (strcmp(StringPt, "dim") == 0) {
dim = integerPt;
Serial.println(F("yup"));
Serial.println(F("*"));
}
}
void light() {
if (dim < 1) {
//Turn TRIAC completely OFF if dim is 0
digitalWrite(AC_pin, LOW);
}
if (dim > 254) { //Turn TRIAC completely ON if dim is 255
digitalWrite(AC_pin, HIGH);
}
if (dim > 0 && dim < 255) {
//Dimming part, if dim is not 0 and not 255
delayMicroseconds(34*(255-dim));
digitalWrite(AC_pin, HIGH);
delayMicroseconds(500);
digitalWrite(AC_pin, LOW);
}
}
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
parseData();
showParsedData();
newData = false;
}
}
sorry i should add that i'm going to use a DS3231 Real Time Clock but i'm not sure how to do the math to create the 510 steps "0-255 up, then 255-0 down" based on the time of day using 24hr time format.
I'm on my mobile, so ..
Just out of curiosity:
delayMicroseconds(34*(255-dim));
What is that? And why do you turn off after 0.5s again?
From what I can see here, you have not implemented your time of day dependency yet.
Your timeline is no symmetrical, 6am to 4 pm is much longer that 4 pm to 8 pm. What would work (you can refine it as you want) to calculate 2 line equations, one for before 4 pm and one for after.
Let's say it easiest to convert the time to minutes passed since midnight. And maybe minutes would be a good scale for the dimming (you can also use hours, ...)
Find the equation for the line that connects the points
(660,10) and (1660,255) and then for (1660,255) and (2060,0)
Then you just add an if statement to check whether it's past 4 pm and then you just run the according equation. It will output the brightness you want and the argument is just the current time passed since midnight.
Again, I'm on mobile, so this is just an example with arb. numbers
dimValue = t*15+10
Where the would be the time in minutes. Something like that should enable you to scale linearily.
Refine the if statement if you want to keep 255 between 3 and 4 pm, but you should be able to figure that out.
Dimming with delayMicroseconds() will just not work. EVERY other thing you try to do will completely mess up the dimming timing. Aka, before you continue, just try to make a dimming routine which isn't blocking.
To add to septillion, probably you should take a look at this.
Now that I am at a desktop, I can provide you with a rough sketch of what I had in mind (if that was your original question). This is more like pseudo code and not meant to be working as is:
// read the time and convert it to minutes since midnight
// something like:
minSinceMidnight = hours*60+minutes;
// where hours and minutes are extracted from the RTC module.
// if statement for dimming
if (minSinceMidnight < 900) {
dim = 10^(-9)*minSinceMidnight^3.5346;
// do your stuff...
}
else if (minSinceMidnight > 900 && < 960) {
dim = 255;
// ...
}
else if (minSinceMidnight > 960 && < 1200) {
dim = -1.0625*minSinceMidnight + 1275:
}
else if (minSinceMidnight > 1200){
dim = 0;
}
else {
// something went wrong
}
Profile would look something like this
de
Dimming with delayMicroseconds() will just not work. EVERY other thing you try to do will completely mess up the dimming timing.
I'm not so sure of that. The OP says the dimming code is working well. The dimming is within a zero cross triggered ISR. I have seen this simple code using delayMicroseconds many times.
It's true that the dimming code will block other parts of the code when the lengthy ISR is executed rather than the other parts of the code will affect the dimming timing.
I do think the triac trigger time of 500 microseconds is way too long.
Non blocking code using the hardware timer is found at Arduino Playground - HomePage
thank you, i have a lot of good information here let me see what i can come up with now. and besides the RTC i'm going to use that i have never used before there wont be any commands expected from serial i just used that to debug some things i'm not sure what to expect using delayMicroseconds(). i'm going to start over with what i have now thanks again
chuckyx:
To add to septillion, probably you should take a look at this.
Now that I am at a desktop, I can provide you with a rough sketch of what I had in mind (if that was your original question). This is more like pseudo code and not meant to be working as is:
// read the time and convert it to minutes since midnight
// something like:
minSinceMidnight = hours*60+minutes;
// where hours and minutes are extracted from the RTC module.
// if statement for dimming
if (minSinceMidnight < 900) {
dim = 10^(-9)*minSinceMidnight^3.5346;
// do your stuff...
}
else if (minSinceMidnight > 900 && < 960) {
dim = 255;
// ...
}
else if (minSinceMidnight > 960 && < 1200) {
dim = -1.0625*minSinceMidnight + 1275:
}
else if (minSinceMidnight > 1200){
dim = 0;
}
else {
// something went wrong
}
Profile would look something like this

this part of the code
// if statement for dimming
if (minSinceMidnight < 900) {
dim = 10^(-9)*minSinceMidnight^3.5346;
// do your stuff...
}
i'm not sure where you got theses numbers. i'm trying the equation in my calculator and im not sure that i'm coming up with the right numbers.could you maybe elaborate a little more the values i was coming up with were a lot higher than 255 for dim.
here is a rough draft of what i have come up with for the loop. i'm not sure where to go from here
#include <Wire.h>
#include <ds3231.h>
ts t; //ts is a struct findable in ds3231.h
int AC_pin = 3;//Pin to OptoTriac
byte dim = 0; //Initial brightness level from 0 to 255, change as you like!
const byte numChars = 50;
char receivedChars[numChars];
char tempChars[numChars];
char StringPt[numChars] = {0};
unsigned long integerPt = 0;
float floatFromPC = 0.0;
boolean newData = false;
unsigned long wait = 0;
int hr = 0;
void setup() {
Serial.begin(115200);
Wire.begin(); //start i2c (required for connection)
DS3231_init(DS3231_INTCN); //register the ds3231 (DS3231_INTCN is the default address of ds3231, this is set by macro for no performance loss)
pinMode(AC_pin, OUTPUT);
attachInterrupt(0, light, FALLING);//When arduino Pin 2 is FALLING from HIGH to LOW, run light procedure!
Serial.println(F("Ready!"));
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
long rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0';
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void parseData() {
char * strtokIndx;
strtokIndx = strtok(tempChars, ",");
strcpy(StringPt, strtokIndx);
strtokIndx = strtok(NULL, ",");
integerPt = atol(strtokIndx);
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx);
}
void showParsedData() {
if (strcmp(StringPt, "dim") == 0) {
dim = integerPt;
Serial.println(F("yup"));
void light();
Serial.println(dim);
Serial.println(F("*"));
}
}
void light() {
if (dim < 1) {
//Turn TRIAC completely OFF if dim is 0
digitalWrite(AC_pin, LOW);
}
if (dim > 254) { //Turn TRIAC completely ON if dim is 255
digitalWrite(AC_pin, HIGH);
}
if (dim > 0 && dim < 255) {
//Dimming part, if dim is not 0 and not 255
delayMicroseconds(20 * (295 - dim));
digitalWrite(AC_pin, HIGH);
delayMicroseconds(50);
digitalWrite(AC_pin, LOW);
}
}
void loop() {
hr = t.hour;
DS3231_get(&t);
if (hr > 6 && hr < 13) {
if (dim < 255) {
if (millis() - wait >= 200) { // not sure how to handle this value
dim++; // not sure how to handle this value
Serial.println(dim);
wait = millis();
}
}
}
if (hr >= 13 && hr < 16) {
dim = 255; //full brightness between 3 and 5 pm
}
if (hr >= 16) {
if (dim > 0) {
dim--;
}
}
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
parseData();
showParsedData();
newData = false;
}
}
the dimming is working perfect but i need help mapping it to the time the very last "if (hr >= 16) {" i want have dim set to zero by time time it gets to hr 20
is there a way to re-write this in millis?
if (dim > 0 && dim < 255) {
//Dimming part, if dim is not 0 and not 255
delayMicroseconds(20 * (295 - dim));
digitalWrite(AC_pin, HIGH);
delayMicroseconds(50);
digitalWrite(AC_pin, LOW);
}
}
is there a way to re-write this in millis?
This makes no sense. Do you mean
Is there a way to re-write this without using delay
Previously pointed out in reply #5
Non blocking code using the hardware timer is found at https://playground.arduino.cc/Main/ACPhaseControl
cattledog:
This makes no sense. Do you mean
Previously pointed out in reply #5
Non blocking code using the hardware timer is found at https://playground.arduino.cc/Main/ACPhaseControl
that code uses delay how is it non blocking
that code uses delay how is it non blocking
The code sample does not use delay() in creating the delayed triac trigger timing from zero cross. That is a major blocking aspect of the code you have, although it is not certain that it will be a problem in your application.
The phase control example does use delay() in the brightness fade/sweep and that is easily replaced by a millis() timer.
Do you not see the difference?
cattledog:
The code sample does not use delay() in creating the delayed triac trigger timing from zero cross. That is a major blocking aspect of the code you have, although it is not certain that it will be a problem in your application.
The phase control example does use delay() in the brightness fade/sweep and that is easily replaced by a millis() timer.
Do you not see the difference?
sorry i ran the code and it does work i'm not sure how the loop part of the code works or what this means "TCNT1 = 65536-PULSE;" i see in the loop i--; i understand that i don't understand OCR1A = i; or what exactly this is doing "if (i<65){i=483;}" and yes the blocking in my original code does cause a lot of problems especially if i'm using millis() to control the brightness. i could see if maybe i used a POT or something it wouldn't be such a problem but otherwise i'm going to have to stick with the AC Phase Control. can you help me better understand this "if (i<65){i=483;} " thanks
For a start, here is Nick Gammon's tutorial in the AVR timers to give you some background material.
what this means "TCNT1 = 65536-PULSE;
That line is advancing the timer close to the reset point to control the pulse length of the triac turn on pulse. It is functionally equivalent(but shorter in time) to your delayMicroseconds(50) with the AC pin HIGH and then LOW. The overflow and timer reset triggers the overflow interrupt which turns the triac pulse off. The triac stays on by itself until the next zero cross point. Study the theory of operation image.
i don't understand OCR1A = i
OCR1A and its compare match interrupt triggering of the ISR(TIMER1_COMPA_vect) code is determining the delay from the zero cross point. Setting OCR1A equal to a value = i controls the turn on delay and the dimming.
what exactly this is doing "if (i<65){i=483;}
It is resetting (back to dim) a dimming sweep which is going from dim to bright.
cattledog:
For a start, here is Nick Gammon's tutorial in the AVR timers to give you some background material.
Gammon Forum : Electronics : Microprocessors : Timers and counters
That line is advancing the timer close to the reset point to control the pulse length of the triac turn on pulse. It is functionally equivalent(but shorter in time) to your delayMicroseconds(50) with the AC pin HIGH and then LOW. The overflow and timer reset triggers the overflow interrupt which turns the triac pulse off. The triac stays on by itself until the next zero cross point. Study the theory of operation image.
OCR1A and its compare match interrupt triggering of the ISR(TIMER1_COMPA_vect) code is determining the delay from the zero cross point. Setting OCR1A equal to a value = i controls the turn on delay and the dimming.
It is resetting (back to dim) a dimming sweep which is going from dim to bright.
thankyou!! i'm going to see what i can come up with and learn more about the interrupts. this is a great start
notsolowki:
i'm not sure where you got theses numbers. i'm trying the equation in my calculator and im not sure that i'm coming up with the right numbers.could you maybe elaborate a little more the values i was coming up with were a lot higher than 255 for dim.
I got them from the fitting in excel, see the trendline equation in the image. I'm kind of confused how you can get even higher numbers when I actually forgot to incorporate the factor of 9 that is present in the image but not in my code.
With more digits (and this time with the factor of ~ 9) it should have read:
dim = 9.2175*10^-9*minSinceMidnight^3.5346;
Are you typing it correctly? Didn't you forget the negative sign? 0.00..0921*minSinceMidnight^3.5346 should be a small number initially.
minSinceMidnight |
dim |
360 |
10 |
610 |
64.52 |
710 |
110.33 |
900 |
255.01 |
where 360 = 6 a.m. and 900 = 3 p.m.