The weatherstation's internals are pretty much complete. I just have to solder the antennas onto the transmitter/receiver and the headers onto the barometric pressure sensor. I'll split this entry into three parts: first I'll go over how I made the antennas, second I'll go over the parts/operation of the transmitter, and third, I'll go over the operation of the receiver.
Antennas
I made quarter wavelength antennas out of wire. The antennas themselves are 1/4 the wavelength of the signal. In my case, the transmitter and receiver operate at a frequency of 433 MHz, so the quarter wavelength in centimeters is
λ = (c/f)*25
Where c is the speed of light (m/s) and f is the frequency (1/s) of the wave. The quarter wavelength in centimeters is therefore:
λ = (3E8 /433E6)*25 = 17.3 cm
This length assumes the wave is traveling as if in a vacuum. I've seen other sources online that use 0.95c instead, therefore making the antenna a bit shorter.
So far, I've been able to get transmissions from about 12 ft away with a clear line of sight. More range tests are needed.
Transmitter
Parts
- Sparkfun Redboard
- 433 MHz transmitter
- DHT Sensor
Show Code
// Author: Zac DeMeo
// Uses modified DHT22 library from https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib
// Uses VirtualWire library from http://www.airspayce.com/mikem/arduino/VirtualWire/
#include <dht.h>
#include <VirtualWire.h>
dht DHT;
short const DELAY = 2000;
unsigned long THEN = millis();
byte const DHT22_PIN = 5;
byte const LED_PIN = 13;
static char data[20];
void setup() {
Serial.begin(115200);
vw_setup(2000); // Set transmission rate
}
void loop() {
unsigned long NOW = millis();
// Execute if statement block every DELAY milliseconds
if ((NOW - THEN) >= DELAY) {
readTemp();
transmit();
THEN = millis();
}
}
// Reads the DHT22 sensor data. Only processes data if sensor returns DHTLIB_OK.
void readTemp() {
int chk = DHT.read22(DHT22_PIN);
if (chk == DHTLIB_OK) {
char sensors[20];
// Retrieve sensor values
double humidity = DHT.humidity;
double temperature = DHT.temperature;
double dpoint = dewpoint(temperature, humidity);
// Convert to Fahrenheit
dpoint = CtoF(dpoint);
temperature = CtoF(temperature);
// Convert sensor values to strings
char humidityBuffer[5];
char temperatureBuffer[5];
char dpointBuffer[5];
dtostrf(humidity, 4, 1, humidityBuffer);
dtostrf(temperature, 4, 1, temperatureBuffer);
dtostrf(dpoint, 4, 1, dpointBuffer);
// Convert to String and concatenate
String buffer = String(humidityBuffer) + String(",") + String(temperatureBuffer) + String(",") + String(dpointBuffer);
buffer.toCharArray(data, 20);
Serial.println(data);
}
}
// Transmits a string using the VirtualWire library
void transmit() {
// Turn on LED to signal beginning of tx
digitalWrite(LED_PIN, true);
// Send buffer containing sensor data
vw_send((uint8_t*)data, strlen(data));
vw_wait_tx(); // Wait for the transmission to complete
// Turn off LED to signal tx completion
digitalWrite(13, false);
delay(200);
}
// Converts temperature from Celsius to Fahrenheit scale
double CtoF(double temp) {
return (temp * 1.8) + 32;
}
// Calculates the dew point in Celsius, from http://andrew.rsmas.miami.edu/bmcnoldy/Humidity.html
double dewpoint(double T, double RH) {
return 243.04 * (log(RH/100) + ((17.625 * T)/(243.04 + T))) / (17.625-log(RH/100)-((17.625 * T) / (243.04 + T)));
}
Refer to my
previous post for details about how the code operates and a schematic to show how the components were hooked up to the Arduino.
Receiver
NOTE: You can't use the Sparkfun RedBoard for the transmitter out of the box because they don't come with the ISP headers populated. The Ethernet shield requires this, so you'll have to solder it on yourself or use an Arduino that already has the headers populated.
Parts
- Arduino Duemilanovae
- Arduino Ethernet shield w/Ethernet cable
- 433 MHz receiver
Show Code
// Author: Zac DeMeo
// Modified from the VirtualWire receiver and Ethernet server examples
#include <VirtualWire.h>
#include <SPI.h>
#include <Ethernet.h>
// Weather data that will be displayed on the webpage.
// Index 0: Relative humidity (%)
// Index 1: Temperature (F)
// Index 2: Estimated dewpoint (F)
char values[3][80];
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);
EthernetServer server(80);
unsigned long time = 0;
void setup() {
Serial.begin(9600);
// Start listening for weatherstation transmissions
vw_setup(2000);
vw_set_rx_pin(2);
vw_rx_start();
// Start the web server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
void loop() {
listenForClients();
listenForTx();
}
void listenForClients() {
EthernetClient client = server.available();
if (client) {
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("");
client.println("<html>");
// Print temperature
client.print("Temperature: ");
client.print(values[1]);
client.print(" °F");
client.println("");
// Print relative humidity
client.print("Relative humidity: ");
client.print(values[0]);
client.print("%");
client.println("");
// Print dewpoint
client.print("Dewpoint: ");
client.print(values[2]);
client.print(" °F");
client.println("");
// Print time measurement was taken.
client.print("Taken at ");
client.print(time);
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}
}
void listenForTx() {
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen)) {
// Get current time
time = millis();
// Turn LED on to show rx
digitalWrite(13, true);
// string to copy buf into
char message[80];
message[0] = '';
// Read buffer into string
int j = 0;
for (int i = 0; i < buflen; i++) {
if (buf[i] != ',') {
int length = strlen(message);
message[length] = buf[i];
message[length + 1] = '';
if (i == (buflen - 1)) {
strcpy(values[j], message);
break;
}
} else {
strcpy(values[j], message);
message[0] = '';
j++;
}
}
// Turn LED off to signify that rx is complete
digitalWrite(13, false);
}
}
How It Works
First, it is
imperative that you call
vw_setup() before calling
server.begin(). There's possibly some sort of timer issue, and if you start the webserver before VirtualWire, nothing will work.
Receiving Weather Data
The receiver listens for transmissions from the transmitter and stores in it
buf. The contents of
buf are then copied into
values, which will always contain the most recent weather data.
Manipulting C strings was definitely the most time consuming part of this project, but definitely the most educational. Arduino does have a String object, which simplifies string operations greatly. However, it will increase the size of your code.
The Webserver
Lines 14-17 set the server MAC address, IP address, and port. If your router requires a particular IP range, you'll want to change line 16. Once a client connects to the webserver, it will display the current data stored in the variable
value. The webserver will also auto-refresh every 5 seconds.
The Arduino EthernetClient library is very straightforward. Think of the
client.println() statement as the web browser version of
Serial.println(). You're essentially printing a line-by-line webpage for your browser.
Future Additions
- Solder antennas to the transmitter and the receiver
- Add my barometric pressure sensor once I solder its headers on
- Do more extensive range testing between the transmitter and the receiver
- Remove the use of the String library from line 55 of my transmitter code. Replace with C strings
- Start designing/constructing enclosures for the transmitter/receiver
- Solder components onto PCBs