Open-source weather station for astronomy
1import time
2import math
3from machine import I2C, Pin, RTC, ADC
4
5try:
6 import usocket as socket
7except:
8 import socket
9
10import dht
11from mlx90640 import MLX90640, RefreshRate, init_float_array
12import tsl2591
13
14# To connect to weather station : http://astroweatherstation/index.html or http://10.42.0.126/index.html
15
16# Define time
17rtc = RTC()
18# Initialize I2C
19i2c = I2C(0, sda=Pin(22), scl=Pin(23), freq=400_000)
20# DHT22 Temperature+Humidity
21DHT22sensor = dht.DHT22(Pin(0))
22# MLX90640 Infrared camera
23MLX90640sensor = MLX90640(i2c)
24MLX90640sensor.refresh_rate = RefreshRate.REFRESH_2_HZ
25ir_frame = init_float_array(768)
26# MH-RD Rain sensor
27MHRDsensor = ADC(Pin(1))
28MHRDsensor.width(ADC.WIDTH_12BIT)
29MHRDsensor.atten(ADC.ATTN_11DB)
30# TSL2591 luminosity
31TSL2591 = tsl2591.TSL2591(i2c=i2c)
32TSL2591.gain = tsl2591.GAIN_HIGH
33TSL2591.integration_time = tsl2591.INTEGRATIONTIME_100MS
34
35def dew_point(tc, rh):
36 # tc: Temperature in Celsius
37 # rh: Relative humidity
38 b = 17.625
39 c = 243.04 # degC
40 gamma = math.log(rh/100) + b*tc/(c+tc)
41 td = c*gamma/(b-gamma)
42 return td
43
44def cutout_frame(data, x, y, size_x, size_y, w=32, h=24):
45 half_x = size_x // 2
46 half_y = size_y // 2
47 cut = []
48 for i in range(-half_x, half_x + 1):
49 for j in range(-half_y, half_y + 1):
50 xi = min(max(x + i, 0), w - 1)
51 yj = min(max(y + j, 0), h - 1)
52 cut.append(data[w*yj + xi])
53 return cut
54
55def mean(l):
56 n = len(l)
57 m = 0
58 for i in range(n):
59 m += l[i]
60 return m/n
61
62# Establish connection
63s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
64try:
65 s.bind(('', 80))
66except OSError:
67 continue
68s.listen(5)
69
70oldsec = -1
71oldmins = -1
72while True:
73 # Get time
74 tnow = rtc.datetime()
75 year = str(tnow[0])
76 month = str(tnow[1]) if tnow[1]>=10 else '0'+str(tnow[1])
77 day = str(tnow[2]) if tnow[2]>=10 else '0'+str(tnow[2])
78 hour = str(tnow[4]) if tnow[4]>=10 else '0'+str(tnow[4])
79 minute = str(tnow[5]) if tnow[5]>=10 else '0'+str(tnow[5])
80 sec = int(tnow[6]) if tnow[6]>=10 else '0'+str(tnow[6])
81 str_date_time = f"{year}-{month}-{day}T{hour}:{minute}:{sec}"
82
83 # Update every 10s
84 if ((int(sec) - int(oldsec) > 10) or (minute != oldmins)):
85 oldsec = sec
86 oldmins = minute
87
88 # DHT22 Temperature+Humidity
89 DHT22sensor.measure()
90 tc = DHT22sensor.temperature()
91 rh = DHT22sensor.humidity()
92 td = dew_point(tc, rh)
93
94 # MLX90640 Infrared camera
95 MLX90640sensor.get_frame(ir_frame)
96 MLX90640sensor.get_frame(ir_frame) # Read twice to solve checkerboard issue
97 ir_center = cutout_frame(ir_frame, 16, 12, 5, 5)
98 temp_sky = mean(ir_center)
99 # Heuristic to check in real conditions
100 if (temp_sky < -8.):
101 clouds = 0
102 elif (temp_sky < 0.):
103 clouds = (temp_sky + 8.)/8
104 else:
105 clouds = 1
106
107 # MHRD Rain sensor
108 rain_sens = MHRDsensor.read()
109 if (rain_sens > 3_000):
110 rain = 0
111 elif (rain_sens > 2_800):
112 rain = -(rain_sens - 3_000)/(3_000-2_800)
113 else:
114 rain = 1
115
116 # TSL2591 luminosity
117 tsl_lux = TSL2591.lux_auto_gain
118 tsl_ir = TSL2591.infrared
119 tsl_vis = TSL2591.visible
120 tsl_full = TSL2591.full_spectrum
121
122 # Write HTML file
123 print(str_date_time)
124 file = open("index.html", "w")
125 file.write('<html>\n<head><title>AstroWeatherStation</title></head>\n<body>\n')
126 file.write('timestamp=' + str_date_time + ' <br />\n')
127 file.write(f"ir_sky={temp_sky:.2f} <br />\n")
128 file.write(f"clouds={clouds:.2f} <br />\n") # WeatherWatcher keyword
129# file.write('forecast={0:.3f}'.format(clouds) + ' <br />\n') # WeatherWatcher keyword
130 file.write(f"temperature={tc:.2f} <br />\n") # WeatherWatcher keyword
131 file.write(f"humidity={rh:.2f} <br />\n") # WeatherWatcher keyword
132 file.write(f"dew_point={td:.2f} <br />\n")
133 file.write(f"rain_sensor={rain_sens:.0f} <br />\n")
134 file.write(f"precip={rain:.2f} <br />\n") # WeatherWatcher keyword
135# file.write(f"wind={wind:.2f} <br />\n") # WeatherWatcher keyword
136# file.write(f"gust={gust:.2f} <br />\n") # WeatherWatcher keyword
137# file.write(f"pressure={pressure:.2f} <br />\n") # WeatherWatcher keyword
138 file.write(f"luminosity={tsl_lux:.5f} <br />\n")
139 file.write('</body>\n')
140 file.write('<ir_image>\n')
141 file.write(f"ir_image={str(ir_frame)} <br />\n")
142 file.write('</ir_image>\n')
143 file.write('</html>\n')
144 file.close()
145
146 # Upload file
147 file = open("index.html", "r")
148 response = file.read()
149 file.close()
150
151 conn, addr = s.accept()
152 print('Got a connection from %s' % str(addr))
153 request = conn.recv(1024)
154 conn.send('HTTP/1.1 200 OK\n')
155 conn.send('Content-Type: text/html\n')
156 conn.send('Connection: close\n\n')
157 conn.sendall(response)
158 conn.close()