Skip to content

Commit 130a38d

Browse files
committed
Merge branch 'develop'
2 parents c5fa794 + 1a6aee1 commit 130a38d

File tree

2 files changed

+115
-112
lines changed

2 files changed

+115
-112
lines changed

README.md

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
![OpenWeatherStation (OWS)](https://github.com/panchazo/open-weather-station/blob/master/docs/img/OpenWeatherStation.png)
22

33
**The Open Weather Station (OWS) is an accessible do-it-yourself weather station solution that aims to be affordable, stable, easy to build and tested in the wild. It evolved from other approaches I have been testing and using in the field since late 2012 to this day.**
4-
54
**Cheers!**
6-
7-
___Eng. Francisco Clariá___
5+
___Eng. Francisco Clariá___
86

97
![OpenWeatherStation Presentation (OWS)](https://github.com/panchazo/open-weather-station/blob/master/docs/img/openweatherstation_presentation.jpg)
108

@@ -49,7 +47,7 @@
4947
* wind gust direction (angle)
5048
* rain (mm)
5149
* temperature (Cº)
52-
* atmospheric/barometric pressure (Pascal)
50+
* absolute atmospheric pressure (Pascal)
5351
* relative humidity (%)
5452
* ambient light (lux)
5553

@@ -195,10 +193,12 @@ Use the following diagram and the list of components to solder each element to t
195193

196194
* the __blue__ is a female header pin to later on plug the HC05, the red circles denote that no connection is needed on those pins so soldering is not actually required
197195

198-
* pay close attention to the capacitors polarity and diodes direction as instructed in the diagram
196+
* pay close attention to the capacitors polarity and diodes direction as instructed in the diagram. I have soldered those in the top side so it is easier to show how I did it in the step by step gallery, but it would be better to solder those in the oposite side if you want to achieve a better looking board.
199197

200198
* [Soldering header pins](https://github.com/panchazo/open-weather-station/tree/master/docs/img/assembly-step-by-step/2%20-%20PCB%20header%20pins)
201199

200+
* You can finish the PCB circuit __after everything is in place and tested__ with a small layer of clear nail polish (varnish) to protect the exposed contacts in order to reduce oxidation.
201+
202202
![OWS pcb](https://github.com/panchazo/open-weather-station/blob/master/docs/img/pcb-components.png)
203203

204204
| Item | assembly code |
@@ -247,9 +247,13 @@ The arduino module, as explained before, sends the data using the HC05 Bluetooth
247247
The status led is just a red or green led that blinks to indicate that the station is working. Therefore I recommend placing the led where it is visible. Wire the led with a female jumper to the “stat” male header pin on the module board (recall to connect the shortest leg of the led to the board pin that is connected to ground).
248248

249249
# Powering arduino module
250-
To power the module connect the Arduino Uno USB cable to the portable power bank, and the power bank to one of the 2 available USB outputs on the wall socket charger module, the remaining output can be used later on to connect the Android usb cable to power the smartphone. If you dont want to use a power bank to keep the module on in the event of power loss then you can simply plug the arduino usb cable directly to the wall socket module.
250+
To power the module connect the Arduino Uno USB cable to the portable power bank, and the power bank to one of the 2 available USB outputs on the wall socket charger module, the remaining output can be used later on to connect the Android usb cable to power the smartphone. If you dont want to use a power bank to keep the module on in the event of power loss then you can simply plug the arduino usb cable directly to the wall socket module. The power bank must comply with the following features:
251+
252+
* must have a power consumption that the charger can supply, so if you buy one that needs 2A (2000mA) input please use a charger that has __at least__ that output power
253+
* the power bank MUST work without user intervention, once plugged, if electricity goes off you wont be there to push any button to turn it on
254+
* lastly, it has to be able to feed the device WHILE charging it at the same time, some power banks will not output power while being charged
251255

252-
The wall socket charger module connects to 110/220v to feed power to the module. I recommend wiring it with the 110/220v power cable so it is easier later on to connect it to any power outlet. You could also accomplish the same using a regular usb wall charger, but please be sure to protect it inside the housing as usually chargers will not work well exposed outdoors for a long period of time.
256+
The wall socket charger module connects to 110/220v to feed power to the module. I recommend wiring it with the 110/220v power cable so it is easier later on to connect it to any power outlet. You could also accomplish the same using a regular usb wall charger, but please be sure to protect it inside the housing as usually chargers will not work well exposed outdoors for a long period of time. I have used this approach when I couldnt find a decent 2A output dual wall socket charger module and used a regular dual usb charger instead (with 2A output).
253257

254258
* [Power the module](https://github.com/panchazo/open-weather-station/tree/master/docs/img/assembly-step-by-step/5%20-%20Wire%20sensors%20and%20power)
255259

@@ -273,7 +277,7 @@ I did my best to organize, add comments the code and keep the code simple to mak
273277

274278
* __ARDUINO_AUTOREBOOT_MINUTES__: once the amount of minutes defined in this constant elapses the arduino will auto reboot and reset the bluetooth chip (turn off and on again). This timer can be restarted by sending a command to the module.
275279

276-
* __SEND_CALCULATED_WIND_SPEED_MS__ and __SEND_CALCULATED_RAIN_MM__: the module will send the cycles per second it counts for anemometer and rain gauge so you can convert this to an actual wind speed or millimeters of rain in the other end and apply your own calibration. If these constants are set to true, the station will perform this calculation internally and send also the calculated windspeed and rain based on default calibrations parameters you can manipulate by changing the following constants:
280+
* __Calibration parameters__: the station module will perform the calculation internally and send windspeed and rain based on default calibrations parameters you can manipulate by changing the following constants:
277281

278282
* __ANEMOMETER_SPEED_FACTOR__, cup anemometer factor, if you don’t know this value leave it as is
279283
* __ANEMOMETER_CIRCUMFERENCE_MTS__, the circumference of a full cycle calculated from the cup centerpoint
@@ -302,4 +306,4 @@ You can send commands (single uppercase ascii character) to make the module do s
302306
* character __L__: send all the measures stored in modules volatile memory for the past WIND_AVG_MINUTE_LOG_SIZE minutes (recall this log is erased after a module reboot and may have been initialized to zero)
303307

304308
# Coming next, the app
305-
__In the upcoming months I will produce an Android app so you can connect to the stations, monitor the parameters in real time, store the samples for long periods of time and see graphics with its evolution and send the information in real time to cloud services. The app will be released as open source too and you will be able to use it as is (apk) or download the code and make your custom flavor of it. Stay tunned!__
309+
__In the upcoming months I will produce an Android app so you can connect to the stations, monitor the parameters in real time, store the samples for long periods of time and see graphics with its evolution and send the information in real time to cloud services. The app will be released as open source too and you will be able to use it as is (apk) or download the code and make your custom flavor of it. Stay tuned!__

arduino/open-weather-station/open-weather-station.ino

+102-103
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
/**********************************************************************
2-
Open Weather Station
2+
Open Weather Station
33
https://OpenWeatherStation.com
44
5-
PROJECT
6-
This software reads data from the sensors and sends the samples via
7-
bluetooth. It has been build a part of a larger solution that
8-
integrates with an external device to connect, store, visualize and
9-
send data to external services.
5+
PROJECT
6+
This software reads data from the sensors and sends the samples via
7+
bluetooth. It has been build a part of a larger solution that
8+
integrates with an external device to connect, store, visualize and
9+
send data to external services.
1010
11-
LICENCE
12-
Copyright 2017 Francisco Claria
11+
LICENCE
12+
Copyright 2017 Francisco Claria
1313
14-
Licensed under the Apache License, Version 2.0 (the "License");
15-
you may not use this file except in compliance with the License.
16-
You may obtain a copy of the License at
14+
Licensed under the Apache License, Version 2.0 (the "License");
15+
you may not use this file except in compliance with the License.
16+
You may obtain a copy of the License at
1717
1818
http://www.apache.org/licenses/LICENSE-2.0
1919
20-
Unless required by applicable law or agreed to in writing, software
21-
distributed under the License is distributed on an "AS IS" BASIS,
22-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23-
See the License for the specific language governing permissions and
24-
limitations under the License.
20+
Unless required by applicable law or agreed to in writing, software
21+
distributed under the License is distributed on an "AS IS" BASIS,
22+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23+
See the License for the specific language governing permissions and
24+
limitations under the License.
2525
***********************************************************************/
2626

2727
/**********************************************************************
@@ -44,8 +44,6 @@ limitations under the License.
4444
CONFIGS
4545
***********************************************************************/
4646

47-
#define SEND_CALCULATED_WIND_SPEED_MS true //meters per second, set to false to only send raw wind cycles count
48-
#define SEND_CALCULATED_RAIN_MM true //millimeters, set to false to only send raw rain cycles count
4947
#define ARDUINO_AUTOREBOOT_MINUTES 30 //if no ack is received within this minutes arduino autoreboots
5048
#define ANEMOMETER_PIN 2 //digital pin 2 (interrupt 0)
5149
#define RAINGAUGE_PIN 3 //digital pin 3 (interrupt 1)
@@ -57,9 +55,7 @@ limitations under the License.
5755
#define BLUETOOTH_SERIAL_SPEED 9600
5856
#define WIND_SAMPLING_SECONDS 5 //from 5 to 15 seconds would be recommended
5957
#define WIND_SAMPLES_SIZE 12 //this value MUST be 60secs/WIND_SAMPLING_SECONDS, eg. 60/6 => 10=WIND_SAMPLES_SIZE
60-
#define WIND_AVG_MINUTE_LOG_SIZE 18 //this MUST be DATA_EQUAL or greater than 1 to store the current sample
61-
#define DATA_SEPARATOR ','
62-
#define DATA_EQUAL '='
58+
#define WIND_AVG_MINUTE_LOG_SIZE 18 //this MUST be equal or greater than 1 to store the current sample
6359

6460
/**********************************************************************
6561
WIND/RAIN CALIBRATION VALUES - SPEED, RAIN & WIND ANGLE
@@ -89,7 +85,7 @@ BME280I2C pressureSensor;
8985
SoftwareSerial bluetooth(BLUETOOTH_SERIAL_RX_PIN, BLUETOOTH_SERIAL_TX_PIN);
9086
BH1750 lightMeter;
9187

92-
volatile unsigned long nextTimeAnemometerInterrupt = 0 , nextTimeRainIterrupt = 0 ; //software debounce variable
88+
volatile unsigned long nextTimeAnemometerInterrupt = 0 , nextTimeRainIterrupt = 0 ; //software debounce variables
9389
volatile int anemometerCyclesCounter = 0, anemometerMinuteCyclesCounter = 0, rainCyclesCounter = 0; //interrupt counters
9490
unsigned long lastMinuteSampleMillis = 0;
9591
boolean enableSendWindPartialSamples = true;
@@ -105,9 +101,9 @@ struct WindSample {
105101

106102
struct SensorsSample {
107103
float temperature;//
108-
float humidity;
104+
float humidity;//%
109105
float pressure;//Pa
110-
unsigned int lux;
106+
int lux;
111107
float windCyclesPerSecond;
112108
int windAngle;
113109
float gustCyclesPerSecond;
@@ -179,23 +175,24 @@ void captureAndSendPartialSample() {
179175
}
180176

181177
void sendWindPartialSample(Stream &port, WindSample ws) {
182-
port.print(F("rw"));
183-
port.print(DATA_EQUAL);
184-
port.print(ws.windCyclesPerSecond);
185-
port.print(DATA_SEPARATOR);
186-
if (SEND_CALCULATED_WIND_SPEED_MS) {
187-
port.print(F("rws"));
188-
port.print(DATA_EQUAL);
189-
port.print(ws.windCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
190-
port.print(DATA_SEPARATOR);
191-
}
192-
port.print(F("ra"));
193-
port.print(DATA_EQUAL);
178+
port.print(F("{"));
179+
port.print(F("\"rt\""));
180+
port.print(F(":"));
181+
port.print(1);
182+
port.print(F(","));
183+
port.print(F("\"ws\""));
184+
port.print(F(":"));
185+
port.print(ws.windCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
186+
port.print(F(","));
187+
port.print(F("\"a\""));
188+
port.print(F(":"));
194189
port.print(ws.windAngle);
195-
port.print(DATA_SEPARATOR);
196-
port.print(F("rms"));
197-
port.print(DATA_EQUAL);
198-
port.println(ws.sampleMillis);
190+
port.print(F(","));
191+
port.print(F("\"ms\""));
192+
port.print(F(":"));
193+
port.print(ws.sampleMillis);
194+
port.print(F("}"));
195+
port.println();
199196
port.flush();
200197
}
201198

@@ -210,26 +207,29 @@ void captureAndSendMinuteSample() {
210207
BME280::PresUnit presUnit(BME280::PresUnit_Pa);
211208
pressureSensor.read(pressureSensorPressure, pressureSensorTemp, pressureSensorHum, tempUnit, presUnit);
212209

213-
if (isnan(pressureSensorPressure)) {
214-
pressureSensorPressure = -1;
210+
if (isnan(pressureSensorPressure) || pressureSensorPressure < 0) {
211+
pressureSensorPressure = -500;
215212
}
216-
if (isnan(pressureSensorTemp)) {
213+
if (isnan(pressureSensorTemp) || pressureSensorTemp < -273 || pressureSensorTemp > 100) {
217214
pressureSensorTemp = -500;
218215
}
219-
if (isnan(pressureSensorHum)) {
220-
pressureSensorHum = -1;
216+
if (isnan(pressureSensorHum) || pressureSensorHum < 0 || pressureSensorHum > 100) {
217+
pressureSensorHum = -500;
221218
}
222219

223220
int avgWindAngle = 0, gustAngle = 0;
224221
float gustCyclesPerSecond = 0;
225222

226223
calcValuesFromWindSamples(windSamples, WIND_SAMPLES_SIZE, avgWindAngle, gustCyclesPerSecond, gustAngle);
227224

225+
int lightLvl = lightMeter.readLightLevel();
226+
lightLvl = (lightLvl > 54612 || lightLvl < 0) ? -500 : lightLvl;
227+
228228
SensorsSample avgMinuteSample = {
229229
pressureSensorTemp,
230230
pressureSensorHum,
231231
pressureSensorPressure,
232-
lightMeter.readLightLevel(),
232+
lightLvl,
233233
windCyclesPerSecond,
234234
avgWindAngle,
235235
gustCyclesPerSecond,
@@ -251,63 +251,56 @@ void captureAndSendMinuteSample() {
251251

252252
void sendFullSamples(Stream &port, SensorsSample * samples, int samplesToSend) {
253253
for (int i = 0; i < samplesToSend; i++) {
254-
port.print(F("t"));
255-
port.print(DATA_EQUAL);
254+
port.print(F("{"));
255+
port.print(F("\"log\""));
256+
port.print(F(":"));
257+
port.print(i);
258+
port.print(F(","));
259+
port.print(F("\"rt\""));
260+
port.print(F(":"));
261+
port.print(0);
262+
port.print(F(","));
263+
port.print(F("\"t\""));
264+
port.print(F(":"));
256265
port.print(samples[i].temperature);
257-
port.print(DATA_SEPARATOR);
258-
port.print(F("h"));
259-
port.print(DATA_EQUAL);
266+
port.print(F(","));
267+
port.print(F("\"h\""));
268+
port.print(F(":"));
260269
port.print(samples[i].humidity);
261-
port.print(DATA_SEPARATOR);
262-
port.print(F("p"));
263-
port.print(DATA_EQUAL);
270+
port.print(F(","));
271+
port.print(F("\"p\""));
272+
port.print(F(":"));
264273
port.print(samples[i].pressure);
265-
port.print(DATA_SEPARATOR);
266-
port.print(F("l"));
267-
port.print(DATA_EQUAL);
274+
port.print(F(","));
275+
port.print(F("\"l\""));
276+
port.print(F(":"));
268277
port.print(samples[i].lux);
269-
port.print(DATA_SEPARATOR);
270-
port.print(F("w"));
271-
port.print(DATA_EQUAL);
272-
port.print(samples[i].windCyclesPerSecond);
273-
port.print(DATA_SEPARATOR);
274-
if (SEND_CALCULATED_WIND_SPEED_MS) {
275-
port.print(F("ws"));
276-
port.print(DATA_EQUAL);
277-
port.print(samples[i].windCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
278-
port.print(DATA_SEPARATOR);
279-
}
280-
port.print(F("wa"));
281-
port.print(DATA_EQUAL);
278+
port.print(F(","));
279+
port.print(F("\"ws\""));
280+
port.print(F(":"));
281+
port.print(samples[i].windCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
282+
port.print(F(","));
283+
port.print(F("\"wa\""));
284+
port.print(F(":"));
282285
port.print(samples[i].windAngle);
283-
port.print(DATA_SEPARATOR);
284-
port.print(F("g"));
285-
port.print(DATA_EQUAL);
286-
port.print(samples[i].gustCyclesPerSecond);
287-
port.print(DATA_SEPARATOR);
288-
if (SEND_CALCULATED_WIND_SPEED_MS) {
289-
port.print(F("gs"));
290-
port.print(DATA_EQUAL);
291-
port.print(samples[i].gustCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
292-
port.print(DATA_SEPARATOR);
293-
}
294-
port.print(F("ga"));
295-
port.print(DATA_EQUAL);
286+
port.print(F(","));
287+
port.print(F("\"gs\""));
288+
port.print(F(":"));
289+
port.print(samples[i].gustCyclesPerSecond / (float)ANEMOMETER_CYCLES_PER_LOOP * (float)ANEMOMETER_CIRCUMFERENCE_MTS * (float)ANEMOMETER_SPEED_FACTOR);
290+
port.print(F(","));
291+
port.print(F("\"ga\""));
292+
port.print(F(":"));
296293
port.print(samples[i].gustAngle);
297-
port.print(DATA_SEPARATOR);
298-
port.print(F("r"));
299-
port.print(DATA_EQUAL);
300-
port.print(samples[i].rainCyclesPerMinute);
301-
port.print(DATA_SEPARATOR);
302-
if (SEND_CALCULATED_RAIN_MM) {
303-
port.print(F("rmm"));
304-
port.print(DATA_EQUAL);
305-
port.print((float)samples[i].rainCyclesPerMinute * (float)RAIN_BUCKET_MM_PER_CYCLE);
306-
port.print(DATA_SEPARATOR);
307-
}
308-
port.print(F("ms"));
309-
port.print(DATA_EQUAL);
310-
port.println(samples[i].sampleMillis);
294+
port.print(F(","));
295+
port.print(F("\"rmm\""));
296+
port.print(F(":"));
297+
port.print((float)samples[i].rainCyclesPerMinute * (float)RAIN_BUCKET_MM_PER_CYCLE);
298+
port.print(F(","));
299+
port.print(F("\"ms\""));
300+
port.print(F(":"));
301+
port.print(samples[i].sampleMillis);
302+
port.print(F("}"));
303+
port.println();
311304
port.flush();
312305
}
313306
}
@@ -328,18 +321,24 @@ void readCmdFromBluetooth() {
328321
}
329322
if (c == 'T') {
330323
timerManager.restartTimer(arduinoAutorebootTimer);
331-
bluetooth.println(F("EXT"));
332-
bluetooth.flush();
324+
if (ENABLE_DEBUG_SERIAL_OUTPUT) {
325+
Serial.println(F("EXT"));
326+
Serial.flush();
327+
}
333328
}
334329
if (c == 'S') {
335330
enableSendWindPartialSamples = true;
336-
bluetooth.println(F("RT1"));
337-
bluetooth.flush();
331+
if (ENABLE_DEBUG_SERIAL_OUTPUT) {
332+
Serial.println(F("RT1"));
333+
Serial.flush();
334+
}
338335
}
339336
if (c == 'Q') {
340337
enableSendWindPartialSamples = false;
341-
bluetooth.println(F("RT0"));
342-
bluetooth.flush();
338+
if (ENABLE_DEBUG_SERIAL_OUTPUT) {
339+
Serial.println(F("RT0"));
340+
Serial.flush();
341+
}
343342
}
344343
if (c == 'L') {
345344
sendFullSamples(bluetooth, avgMinuteSamplesLog, WIND_AVG_MINUTE_LOG_SIZE);

0 commit comments

Comments
 (0)