|
Forum Index : Microcontroller and PC projects : Micromite Night Light
| Author | Message | ||||
| cdeagle Senior Member Joined: 22/06/2014 Location: United StatesPosts: 266 |
This post describes an automatic Micromite-based night light. It determines the time to turn a night light on and off using an algorithm that computes rise and set of the sun for any day of the year. The following is a picture of the night light. The RTC and LED are on the left. The CircuitGizmos CGMICROBOARD1 Micromite and Mad Evil Scientist simple relay shield are on the right. The red LED on the right header is a status LED that flashes every 30 seconds to indicate the algorithm is running.
Here's the MMBasic source code for this project. It is "hardwired" with the geographic coordinates for Cape Canaveral, Florida and Eastern Standard Time. The algorithm is based on the one published in the Astronomical Almanac and Ed William's aviation page (http://williams.best.vwh.net/). This numerical method computes "local civil time" of sun rise and set. Therefore, do not include the effect of Daylight Savings Time when setting the RTC. The program assumes north latitudes and east longitudes are positive. South latitudes and west longitudes are negative. option autorun on ' night_light.bas ' April 24, 2015 ' MMBasic version 4.6B ' automatic, time adjusting night light using the ' Circuit Gizmos CGMICROBOARD1, the Evil Mad Scientist ' simple relay shield, the Voltaic Systems high ' intensity USB Flashlight, Flexlight or Touchlight, ' and any Micromite-compatible RTC ' www.circuitgizmos.com ' www.shopevilmafscientist.com ' www.voltaicsystems.com ' williams.best.vwh.net ' RTC pin-to-CPU pin wiring ' ------------------------- ' SCL ==> 17, SDA ==> 18 (28 pin) ' SCL ==> 44, SDA ==> 1 (44 pin) ' relay shield ==> pin 42 ' status LED ==> pin 14 ''''''''''''''''''''''' ' IMPORTANT! set the RTC to local "standard" civil time ' do not include DST when setting the RTC ' ======================= ' Cape Canaveral, Florida ' ======================= ' time zone tzone = -5 ' geographic latitude (degrees) obslat = 28.0 + 23.0 / 60.0 + 46.0 / 3600.0 ' geographic longitude (degrees; positive east, negative west) obslong = -(80.0 + 35.0 / 60.0 + 54.0 / 3600.0) ' Sun's zenith for sunrise/sunset ' ------------------------------- ' official = 90 degrees 50' ' civil = 96 degrees ' nautical = 102 degrees ' astronomical = 108 degrees ' use offical zenith (degrees) zenith = 90.0 + 50.0 / 60.0 ' initialize relay "coil energized" pin setpin 42, dout ' initialize status LED setpin 14, dout do ' turn on status LED pin(14) = 1 ' extract current epoch from RTC rtc gettime hours = val(left$(time$, 2)) minutes = val(mid$(time$, 4, 5)) month = val(mid$(date$, 4, 5)) day = val(left$(date$, 2)) year = val(mid$(date$, 7, 10)) ' current local civil time (hours) lct_hours = hours + minutes / 60.0 ' current day-of-the year day_of_year(month, day, year, current_doy) ' compute local civil time of sunrise (hours) rsflag% = 1 riseset(month, day, year, lct_rise) ' compute local civil time of sunset (hours) rsflag% = 0 riseset(month, day, year, lct_set) ' determine state of night light if (lct_hours >= lct_rise and lct_hours <= lct_set) then pin(42) = 0 else pin(42) = 1 endif ' turn off status LED pin(14) = 0 ' pause 30 seconds between calculations pause 30000 loop end '''''''''''''''''''''''''''''''' sub riseset(month, day, year, lct) ' compute rise and set times of the sun ' Almanac for Computers, 1990 ' published by Nautical Almanac Office ' United States Naval Observatory ' Washington, DC 20392 ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' lct = local civil time of sun rise or set '''''''''''''''''''''''''''''''''''''''''''' ' calculate the day of the year day_of_year(month, day, year, n) ' convert observer longitude to hour value longHour = obslong / 15.0 ' calculate an approximate event time if (rsflag% = 1) then ' rising time t = n + ((6.0 - longHour) / 24.0) else ' setting time t = n + ((18.0 - longHour) / 24.0) endif ' Sun's mean anomaly (degrees) manom = 0.9856 * t - 3.289 ' Sun's true longitude (degrees) L = mod360(manom + (1.916 * sin(rad(manom))) + (0.020 * sin(rad(2.0 * manom))) + 282.634) ' right ascension of the sun (degrees) rasc = mod360(deg(atn(0.91764 * tan(rad(L))))) ' right ascension value needs to be in the same quadrant as L Lquadrant = (cint(L / 90.0)) * 90.0 RAquadrant = (cint(rasc / 90.0)) * 90.0 rasc = rasc + (Lquadrant - RAquadrant) ' convert right ascension to hours rasc = rasc / 15.0 ' sine and cosine of the declination of the sun sinDec = 0.39782 * sin(rad(L)) cosDec = cos(asin(sinDec)) ' cosine of the local hour angle of the sun cosH = (cos(rad(zenith)) - (sinDec * sin(rad(obslat)))) / (cosDec * cos(rad(obslat))) if (cosH > 1.0) then print "the sun never rises on this location on this date" return endif if (cosH < -1.0) then print "the sun never sets on this location on this date" return endif ' finish calculating local hour angle of the sun if (rsflag% = 1) then ' rising time H = 360.0 - deg(acos(cosH)) else ' setting time H = deg(acos(cosH)) endif ' convert local hour angle into hours H = H / 15.0 ' calculate local mean time lmt = H + rasc - (0.06571 * t) - 6.622 ' calculate UTC utc = mod24(lmt - longHour) ' determine local civil time of rise or set (hours) lct = mod24(utc + tzone) end sub '''''''''''''''''''''''''''''''''''' Sub day_of_week(month, day, year, dow) ' calculate day of the week using ' TZAdvantage's (modified) algorithm ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' dow = day of the week '''''''''''''''''''''''' a = Int((14-month) / 12) m = month + 12 * a - 2 y = year - a d = day dow = (d + y + Int(y / 4) - Int(y / 100) + Int(y / 400) + Int(31 * m / 12)) Mod 7 End Sub '''''''''''''''''''''''''''''''''''' sub day_of_year(month, day, year, doy) ' calculate day of the year ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' doy = day of the year '''''''''''''''''''''''' n1 = fix(275.0 * month / 9.0) n2 = fix((month + 9.0) / 12.0) n3 = (1.0 + fix((year - 4.0 * fix(year / 4.0) + 2.0) / 3.0)) doy = n1 - (n2 * n3) + day - 30.0 end sub '''''''''''''''' FUNCTION mod360(x) ' modulo 360 degrees function ''''''''''''''''''''''''''''' a = x - 360.0 * FIX(x / 360.0) IF (a < 0.0) THEN a = a + 360.0 mod360 = a END FUNCTION ''''''''''''''' FUNCTION mod24(x) ' modulo 24 hours function '''''''''''''''''''''''''' a = x - 24.0 * FIX(x / 24.0) IF (a < 0.0) THEN a = a + 24.0 mod24 = a END FUNCTION '''''''''''''' FUNCTION asin(x) ' inverse sine function ' input ' x = function argument ' output ' asin = inverse sine of x (radians) ''''''''''''''''''''''''''''''''''''' IF (ABS(x) >= 1.0) then asin = SGN(x) * 0.5 * pi ELSE asin = ATN(x / SQR(1.0 - x * x)) ENDIF END FUNCTION '''''''''''''' FUNCTION acos(x) ' inverse cosine function ' input ' x = function argument ' output ' acos = inverse cosine of x (radians) ''''''''''''''''''''''''''''''''''''''' pidiv2 = 0.5 * pi IF (ABS(x) >= 1.0) then a = SGN(x) * pidiv2 ELSE a = ATN(x / SQR(1.0 - x * x)) END IF acos = pidiv2 - a END FUNCTION This algorithm could also be used to position a photovoltaic system. Simply add the calculations for azimuth and elevation of the sun and drive servos that point the device. |
||||
| viscomjim Guru Joined: 08/01/2014 Location: United StatesPosts: 925 |
Very interesting code. I will have a go at it. Thanks for the contribution. |
||||
| cdeagle Senior Member Joined: 22/06/2014 Location: United StatesPosts: 266 |
I have updated the automatic night light project to use the Explore-28 module and the latest version of MMBASIC. Here's the updated MMBASIC source code; option autorun on ' night_light.bas ' July 7, 2017 ' MMBasic version 5.04.05 ' automatic, time adjusting night light using the ' Explore-28, the Evil Mad Scientist simple relay ' shield, the Voltaic Systems high intensity USB ' Flashlight, Flexlight or Touchlight, and any ' Micromite-compatible RTC ' www.shopevilmafscientist.com ' www.voltaicsystems.com ' williams.best.vwh.net ' RTC pin-to-CPU pin wiring ' ------------------------- ' SCL ==> 17, SDA ==> 18 (28 pin) ' SCL ==> 44, SDA ==> 1 (44 pin) ' relay shield ==> pin 15 ' status LED ==> pin 14 ''''''''''''''''''''''' ' IMPORTANT! set the RTC to local "standard" civil time ' do not include DST when setting the RTC ' ======================= ' Cape Canaveral, Florida ' ======================= ' time zone tzone = -5 ' geographic latitude (degrees) obslat = 28.0 + 23.0 / 60.0 + 46.0 / 3600.0 ' geographic longitude (degrees; positive east, negative west) obslong = -(80.0 + 35.0 / 60.0 + 54.0 / 3600.0) ' Sun's zenith for sunrise/sunset ' ------------------------------- ' official = 90 degrees 50' ' civil = 96 degrees ' nautical = 102 degrees ' astronomical = 108 degrees ' use offical zenith (degrees) zenith = 90.0 + 50.0 / 60.0 ' initialize relay "coil energized" pin setpin 15, dout ' initialize status LED setpin 14, dout do ' turn on status LED pin(14) = 1 ' extract current epoch from RTC rtc gettime hours = val(left$(time$, 2)) minutes = val(mid$(time$, 4, 5)) month = val(mid$(date$, 4, 5)) day = val(left$(date$, 2)) year = val(mid$(date$, 7, 10)) ' current local civil time (hours) lct_hours = hours + minutes / 60.0 ' compute local civil time of sunrise (hours) rsflag% = 1 riseset(month, day, year, lct_rise) ' compute local civil time of sunset (hours) rsflag% = 0 riseset(month, day, year, lct_set) ' determine state of night light if (lct_hours >= lct_rise - 0.25 and lct_hours <= lct_set + 0.25) then pin(15) = 0 else pin(15) = 1 endif ' turn off status LED pin(14) = 0 ' pause 10 seconds between calculations pause 10000 loop end '''''''''''''''''''''''''''''''' sub riseset(month, day, year, lct) ' compute rise and set times of the sun ' Almanac for Computers, 1990 ' published by Nautical Almanac Office ' United States Naval Observatory ' Washington, DC 20392 ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' lct = local civil time of sun rise or set '''''''''''''''''''''''''''''''''''''''''''' ' calculate the day of the year day_of_year(month, day, year, n) ' convert observer longitude to hour value longHour = obslong / 15.0 ' calculate an approximate event time if (rsflag% = 1) then ' rising time t = n + ((6.0 - longHour) / 24.0) else ' setting time t = n + ((18.0 - longHour) / 24.0) endif ' Sun's mean anomaly (degrees) manom = 0.9856 * t - 3.289 ' Sun's true longitude (degrees) L = manom + (1.916 * sin(rad(manom))) + (0.020 * sin(rad(2.0 * manom))) + 282.634 if (L < 0.0) then L = 360.0 + L if (L > 360.0) then L = L - 360.0 ' right ascension of the sun (degrees) rasc = deg(atn(0.91764 * tan(rad(L)))) if (rasc < 0.0) then rasc = 360.0 + rasc if (rasc > 360.0) then rasc = rasc - 360.0 ' right ascension value needs to be in the same quadrant as L Lquadrant = (int(L / 90.0)) * 90.0 RAquadrant = (int(rasc / 90.0)) * 90.0 rasc = rasc + (Lquadrant - RAquadrant) if (rasc < 0.0) then rasc = 360.0 + rasc if (rasc > 360.0) then rasc = rasc - 360.0 ' convert right ascension to hours rasc = rasc / 15.0 ' sine and cosine of the declination of the sun sinDec = 0.39782 * sin(rad(L)) cosDec = cos(asin(sinDec)) ' cosine of the local hour angle of the sun cosH = (cos(rad(zenith)) - (sinDec * sin(rad(obslat)))) / (cosDec * cos(rad(obslat))) if (cosH > 1.0) then print "the sun never rises on this location on this date" return endif if (cosH < -1.0) then print "the sun never sets on this location on this date" return endif ' finish calculating local hour angle of the sun if (rsflag% = 1) then ' rising time H = 360.0 - deg(acos(cosH)) else ' setting time H = deg(acos(cosH)) endif ' convert local hour angle into hours H = H / 15.0 ' calculate local mean time lmt = H + rasc - (0.06571 * t) - 6.622 ' calculate UTC utc = lmt - longHour if (utc < 0.0) then utc = 24.0 + utc if (utc > 24.0) then utc = utc - 24.0 ' determine local civil time of rise or set (hours) lct = utc + tzone if (lct < 0.0) then lct = 24.0 + lct if (lct > 24.0) then lct = lct - 24.0 end sub '''''''''''''''''''''''''''''''''''' Sub day_of_week(month, day, year, dow) ' calculate day of the week using ' TZAdvantage's (modified) algorithm ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' dow = day of the week '''''''''''''''''''''''' a = Int((14-month) / 12) m = month + 12 * a - 2 y = year - a d = day dow = (d + y + Int(y / 4) - Int(y / 100) + Int(y / 400) + Int(31 * m / 12)) Mod 7 End Sub '''''''''''''''''''''''''''''''''''' sub day_of_year(month, day, year, doy) ' calculate day of the year ' input ' month = calendar month ' day = calendar day ' year = calendar year ' output ' doy = day of the year '''''''''''''''''''''''' n1 = fix(275.0 * month / 9.0) n2 = fix((month + 9.0) / 12.0) n3 = (1.0 + fix((year - 4.0 * fix(year / 4.0) + 2.0) / 3.0)) doy = n1 - (n2 * n3) + day - 30.0 end sub '''''''''''''''' FUNCTION mod360(x) ' modulo 360 degrees function ''''''''''''''''''''''''''''' a = x - 360.0 * FIX(x / 360.0) IF (a < 0.0) THEN a = a + 360.0 mod360 = a END FUNCTION ''''''''''''''' FUNCTION mod24(x) ' modulo 24 hours function '''''''''''''''''''''''''' a = x - 24.0 * FIX(x / 24.0) IF (a < 0.0) THEN a = a + 24.0 mod24 = a END FUNCTION |
||||
| cdeagle Senior Member Joined: 22/06/2014 Location: United StatesPosts: 266 |
Here's a version of the night light MMBASIC source code for the Micromite+. It uses a subset of the MMBASIC software that calculates sun rise and set. It is more accurate and the logic for night light on or off is straightforward. ' initialize rise/set flag rsflag% = 0 do ' turn on status LED pin(13) = 1 ' extract current UTC epoch from RTC rtc gettime utc_hours = val(left$(time$, 2)) utc_minutes = val(mid$(time$, 4, 5)) utc_seconds = val(right$(time$, 2)) cmonth = val(mid$(date$, 4, 5)) cday = val(left$(date$, 2)) + utc_hours / 24.0 + utc_minutes / 1440.0 + utc_seconds / 86400.0 cyear = val(mid$(date$, 7, 10)) ' compute utc julian day julian(cmonth, cday, cyear, jdutc) ' compute tdb julian date utc2tdb(jdutc, jdtdb) ' compute current topocentric elevation rsfunc(jdtdb, fx) if (fx >= 0.0 and rsflag% = 1) then ' turn off pin(15) = 0 rsflag% = 0 end if if (fx < 0.0 and rsflag% = 0) then ' turn on pin(15) = 1 rsflag% = 1 end if ' turn off status led pin(13) = 0 ' pause 10 seconds between calculations pause 10000 loop The RTC should be set to UTC time. No time zone or DST need be included. The code that computes the "true" elevation of the sun sub rsfunc(jdtdb, fx) ' rise/set objective function ''''''''''''''''''''''''''''' local rsun(3), gast, azim, elev local dis, dref, sdia ' compute eci position vector and distance of the sun sun(jdtdb, rsun()) dis = vecmag(rsun()) / aunit ' compute topocentric coordinates of the sun tdb2utc(jdtdb, jdutc) gast2(jdutc, gast) eci2topo(gast, rsun(), azim, elev) ' correct for horizontal refraction dref = (34.0 / 60.0) ' correct for semidiameter of the sun (radians) tsdia = asin((0.5 * 696000.0 / aunit) / dis) ' evaluate objective function fx = rtd * elev + tsdia + dref end sub corrects for the semidiameter of the sun and horizontal refraction. Here's a zip archive of NIGHTLIGHT_PLUS.BAS 2018-10-05_050109_nightlight_plus.zip |
||||
| cdeagle Senior Member Joined: 22/06/2014 Location: United StatesPosts: 266 |
Here's a Picomite version of the nightlight. In this picture, an OONO relay module is on the left, the Pico in the middle and a Waveshare RTC to the right. Power switched by the relay is taken from the OONO VBUS pin as is a ground connection for the LED light. Pico pin 1 controls the relay and the heartbeat LED on the Pico is flashed every 10 seconds by the MMBASIC code to indicate the software is running. ![]() Components are stacked from bottom to top as relay, RTC and Pico as shown in this picture. The LED is connected via a small USB breakout board. Everything mounted on a small perf-board I had in a circuit board drawer. |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10610 |
Love the Waveshare modules - they make really nice stuff I'd forgotten about Ed William's work, I used it to develop a flight planning application for my personal use way back when. It is a great source for things spherical trig |
||||
| CaptainBoing Guru Joined: 07/09/2016 Location: United KingdomPosts: 2171 |
I have been manufacturing a dusk2dawn light for a while which uses an SSR to switch a mains load. Whereas the routine to calculate the sunset/rise times above is very precise, my method was to calculate the times by interpolating between 13 data statements (much smaller & faster). It is a blunt instrument but as I use "civil twilight" it doesn't have to be terribly accurate. in practice it is about +/- 5 minutes and the light comes on as you would expect. It is nice not to have to either adjust timers or use light sensing - a purely computed decision. And the "fuzziness" (by which I mean it uses twilight not darkness) of the switch on time covers drift in the RTC. The prototype has been operational for years and I haven't tweaked the clock once. here is a pic of an earlier version PCB, RTC on the top, uM on the bottom. It really is plug in and forget. ![]() JP3 is a header for a RCWL0516 microwave movement sensor, (which provides the 3V3 from the 5V6 Zener voltage) and movement detection. The light comes on at dusk and goes off at 23:00(non DST) and will come on for a period if movement is detected (having been de-glitched - I like the '0516 - much greater range that IR jobbies and can see thru walls - but it is affected by bushes moving in the wind). The later version has a neater PSU and jumpers for no RCWL0516 so giving true d2d if required. JP1 is a combined console and flashing connector. One "drawback"; the unit must be connected through an isolation transformer when on the bench as any mains powered device will trip the MCB as soon as you connect the ground reference - a "feature" of the PSU design. No such problem for handheld kit. The IsDaylight() function can be found here. In its simplest form, it returns a flag so IsDayLight(Now()) gives a 0 or 1 . Edited 2021-07-02 19:07 by CaptainBoing |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |