Sun Set Clock
Technology separates us from nature, but does it need to? I used some of my stay at theĀ Digital Naturalism Conference in Thailand to prototype a clock that determines local time of day from sunlight to promote a natural sense of timekeeping.
By using technology to encourage human relationships with nature, I hope to highlight that machines can encourage us to be *more* human and organic rather than slowly making people irrelevant. As a counterpoint to consuming industrialized time we can also obtain time from scratch, regaining control of the very pacing that drives our lives. The Sun Set Clock uses local solar time, therefore noon is when the sun is at apogee at our location. This is how time used to be measured, before telegraphs and transcontinental trains required a move to time zones, where the clock and the sun no longer match. This system isn’t concerned with exactitude–there’s plenty of systems to do that if you need it. Instead this clock can be used to mark the general progress of the day rather than creating anxiety around how every minute is used.
Sun Set Clock in its natural environment
The initial prototype uses light level changes to detect sunrise and sunset, with local noon being the point exactly between these two events. When the clock starts, it makes its best estimate of the time. For example, if it’s dark at startup, the clock assumes that it’s midnight because that’s the best guess you can make without more information. At sunrise, this corrects to 6 am (a higher-quality guess) and then at sunset it will correct to the proper local time (not time zone time but astronomical time at your precise location). All of this works, although it’s still a bit fragile–operating best in full view of the sky on a relatively sunny day. Dark clouds, deep shadows and porch lights can confuse it, so these will need to be addressed in a future version. For now, I’m enjoying what I think of as “some time of my own.” I hope you enjoy it too.
Sun Set Clock – Instructions for Use
Sun Set Clock, using BBC micro:bit and Grove light sensor
PARTS LIST
- BBC Micro:bit
- Grove Shield for Micro:bit
- Grove light sensor
- Battery holder
- 2 AA batteries
- USB micro cable
CODE (also on GitHub)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | # Uses a BBC micro:Bit with Grove shield and light sensor to # create a clock that sets itself using the sun. # Rob Faludi, faludi.com, June 2018 from microbit import * from math import trunc day_length = 86400000 daylight_threshold = 60 hysteresis = 10 midnight = running_time() sunrise = None sunset = None noon = None daytime_length = None light_array = [] light_average = 0 daytime_length_array = [] ctr = 0 d_ctr = 0 update_interval = 15 * 60 * 1000 / 100 # in milliseconds last_update = 0 display_clear_time = 0 last_daytime_length = 0 print("Sun Set Clock 1.01 Start...") def get_time(): day_time = ((running_time() - midnight) % day_length) / 1000 hours = trunc(day_time / 3600) minutes = trunc((day_time % 3600) / 60) seconds = trunc(day_time % 60) return [hours, minutes, seconds] def get_time_string(show_seconds=False): hours, minutes, seconds = get_time() if hours < 10: hours = "0" + str(hours) else: hours = str(hours) if minutes < 10: minutes = "0" + str(minutes) else: minutes = str(minutes) time_string = (hours + ":" + minutes) if show_seconds: if seconds < 10: seconds = "0" + str(seconds) else: seconds = str(seconds) time_string = (hours + ":" + minutes + ":" + seconds) return time_string def read_light_sensor(): light = pin0.read_analog() return light def setting_mode(threshold): while (button_a.is_pressed() or button_b.is_pressed()): display.show(Image.CLOCK12) sleep(300) display.show(Image.CLOCK3) sleep(300) display.show(Image.CLOCK6) sleep(300) display.show(Image.CLOCK9) sleep(300) display.clear() display.show(threshold) sleep(300) display.show(Image.ARROW_E) sleep(400) display.show(Image.ARROW_W) sleep(400) display.clear() last_click = running_time() while (running_time() < last_click + 5000): if button_b.was_pressed(): last_click = running_time() threshold = threshold + 1 display.show(Image.ARROW_N) sleep(300) display.show(threshold) sleep(300) display.clear() if button_a.was_pressed(): last_click = running_time() threshold = threshold - 1 display.show(Image.ARROW_S) sleep(300) display.show(threshold) sleep(300) display.clear() tfile = open("threshold_storage", "w") tfile.write(str(threshold)) tfile.close() display.show(Image.YES) sleep(700) display.clear() return(threshold) # MAIN PROGRAM # update daylight threshold from flash storage try: tfile = open("threshold_storage", "r") daylight_threshold = int(tfile.read()) tfile.close() except OSError: print("No threshold file found") last_light = read_light_sensor() # guess day or night on startup if read_light_sensor() > daylight_threshold: midnight = running_time() - (12 * 60 * 60 * 1000) else: midnight = running_time() while True: # set current thresholds sunrise_threshold = daylight_threshold + (hysteresis/2) sunset_threshold = daylight_threshold - (hysteresis/2) # display the time when the left button is pressed if button_a.was_pressed(): display.show(get_time_string()) display.clear() print("Time:", get_time_string(show_seconds=True)) # debug output when the right button is pressed if button_b.was_pressed(): print("light_average", light_average) display.show(str(round(light_average))) sleep(300) display.clear() # go into setting mode if the right button is held down if button_b.is_pressed(): press_time = running_time() while (button_b.is_pressed()): display.show(Image.DIAMOND_SMALL) if (running_time() - press_time > 1500): daylight_threshold = setting_mode(daylight_threshold) display.clear() # read light levels periodically and update clock as needed if (running_time() > (last_update + update_interval) or last_update == 0): try: light_array[ctr] = read_light_sensor() except IndexError: light_array.append(read_light_sensor()) ctr += 1 if ctr >= 100: ctr = 0 light_average = sum(light_array) / len(light_array) # detect sunrise and sunset if light_average >= sunrise_threshold and last_light < sunrise_threshold: # guess morning on first sunrise if sunrise is None: midnight = running_time() - (6 * 60 * 60 * 1000) sunrise = running_time() print("Sunrise detected at: ", sunrise) display.show(Image.ARROW_N) display_clear_time = sunrise + 15 * 60 * 1000 if light_average <= sunset_threshold and last_light > sunset_threshold: # guess evening on first sunset if sunset is None: midnight = running_time() - (18 * 60 * 60 * 1000) sunset = running_time() print("Sunset detected at: ", sunset) display.show(Image.ARROW_S) display_clear_time = sunset + 15 * 60 * 1000 last_light = light_average # clear display when needed if display_clear_time > 0 and running_time() > display_clear_time: display.clear() display_clear_time = 0 # set the clock if sunrise is not None and sunset is not None: if sunset-sunrise > 0: daytime_length = sunset - sunrise # else: # nighttime_length = sunrise - sunset # daytime_length = day_length - nighttime_length if daytime_length != last_daytime_length: try: daytime_length_array[d_ctr] = daytime_length except IndexError: daytime_length_array.append(daytime_length) d_ctr += 1 if d_ctr >= 7: d_ctr = 0 daytime_length_average = sum(daytime_length_array) / len(daytime_length_array) last_daytime_length = daytime_length print("Daytime length average:", daytime_length_average) noon = sunset - (daytime_length_average / 2) midnight = noon - (day_length / 2) # schedule for next update last_update = running_time() |
