Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 09:18 19 Nov 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Micromite Night Light

Author Message
cdeagle
Senior Member

Joined: 22/06/2014
Location: United States
Posts: 266
Posted: 08:01am 26 Apr 2015
Copy link to clipboard 
Print this post

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 States
Posts: 925
Posted: 12:08pm 27 Apr 2015
Copy link to clipboard 
Print this post

Very interesting code. I will have a go at it. Thanks for the contribution.

 
cdeagle
Senior Member

Joined: 22/06/2014
Location: United States
Posts: 266
Posted: 12:44am 07 Jul 2017
Copy link to clipboard 
Print this post

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 States
Posts: 266
Posted: 07:01pm 04 Oct 2018
Copy link to clipboard 
Print this post

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 States
Posts: 266
Posted: 06:28pm 01 Jul 2021
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 10610
Posted: 09:41pm 01 Jul 2021
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 2171
Posted: 08:40am 02 Jul 2021
Copy link to clipboard 
Print this post

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
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025