In my previous blog posts I mainly focused on the IoT aspects of the project. In Old meets new, the 1-Wire Weather Station on the SPARK Core. (part 2) I showed a scan of the 1-Wire bus, which showed the devices in the 1-Wire Weather Station. In the last one Old meets new, the 1-Wire Weather Station on the SPARK Core. (part 4) I showed how two extra 1-Wire temperature sensors ar read and published in the cloud. In Old meets new, the 1-Wire Weather Station on the SPARK Core. (part 1) I very briefly introduced you to the Weather Station. It's now time to go into more detail into the hardware and the software to read out the weather data.
The description of the hardware is based on a document written by Dan Awtrey from Dallas Semiconductor.(http://www.midondesign.com/Documents/1wire_weather_stn.PDF)
1-Wire Weather Station
A diagram of the basic 1-Wire weather station is shown below:
Eight compass points are directly measured with eight reed switches actuated by a magnet attached to the weather vane axle. Whenever the magnet is between two adjacent reeds both switches close. This supplies an additional eight directions, for a total of sixteen compass points. Note that the bus master can only read wind direction when the DS2407 addressable switch is closed. Similarly, a magnet attached to the wind cups operates a reed switch connected to the DS2423 counter chip that keeps track of the total number of revolutions the cups make and provides the data to the bus master on demand. Ambient temperature is measured with a DS1820 Digital Thermometer. Below is the detailed schematic.
As you can see in the pictures, all 1-Wire devices are connected in parallel to the 1-Wire bus. By issuing a search command on the bus one can find the unique addresses of all these chips:
void findDevices() { uint8_t addr[12]; int found = 0; int counter; while(one.search(addr)) { Serial.print("Found device: "); char *owID = new char[24]; sprintf(owID, "%02x%02x%02x%02x%02x%02x%02x%02x", addr[0], addr[1], addr[2] , addr[3] , addr[4] , addr[5], addr[6], addr[7] ); sensors[found].id = owID; for(int i=0;i<8;i++) { sensors[found].rom[i] = addr[i]; } sensors[found].updated = 0; Serial.print(owID); if (addr[0] == 0x22) { //ds1822 temp sensor getTempBytes(sensors[found].rom); sensors[found].value = getTemp('C',12); Serial.print(" Temperature: " + String(getTemp('C',12))); sensors[found].updated = millis(); } if (addr[0] == 0x10) { // DS1820 temp sensor getTempBytes(sensors[found].rom); sensors[found].value = getTemp('C',9); Serial.print(" Temperature: " + String(sensors[found].value)); sensors[found].updated = millis(); } if (addr[0] == 0x1D) { //ds2423 counter counter = readCounter(sensors[found].rom,2); uint32_t now = millis(); sensors[found].value = ((counter - oldcounter) *1000/(now - timestamp) / 2.0) ; // in m/s ???? Serial.print(" Windspeed: " + String(sensors[found].value)); sensors[found].updated = now; timestamp = now; oldcounter = counter; } if (addr[0] == 0x12) { // DS2407 switch setSwitch(sensors[found].rom,dirswitch); sensors[found].value = dirswitch; Serial.print(" Wind Direction: " + String(winddir)); sensors[found].updated = millis(); dirswitch = !dirswitch; } Serial.println(""); found++; } for (int j = found; j<NUM_SENSORS;j++){ sensors[j].id = NULL; } }
Each found device is put into an array of sensor structs:
class OWSensor { public: char *id ; uint8_t rom[8]; float value ; int updated ; };
The first 8 bit (1 byte or 2 Hex characters) of the unique device address identifies the type of 1-wire device. See the family code list for the types.
Based on the family code different routines are used for measuring the specific feature.
Temperature
Ambient temperature is measured with the DS1820. This self contained sensor measures temperature as the difference between two oscillators, one of which is temperature dependent. The sensor functions over a -55 to 125°C range and provides plus and minus 0.5°C uncorrected accuracy over 0 to +70 °C. Normally this sensor would be mounted in a separate properly ventilated housing in order to measure ambient temperature as closely as possible. However, the initial design called for a single housing to simplify installation, so the sensor was mounted on the weather station PCB. Since exposure to the sun can raise the inside of the housing as much as 20 degrees higher than ambient, this arrangement can lead to large errors in the reading for many applications. In order to obtain a more accurate measure of ambient temperature an additional DS1820 can be mounted in a suitable separate enclosure and added to the bus by plugging into the daisy-chain connector provided on the PCB. Two functions are used for reading the temperature, getTemp for reading the temperature in Fahrenheit or Celcius and getTempBytes for reading the raw value from the sensor. Temperature can be read in 0.0625 or 0.25 degrees precision, depending on the sensor type used. Here I'm using the DS1820 on the Weather Station PCB, and a DS1822 close to the Spark Core.
void getTempBytes(uint8_t *rom) { one.reset(); one.select(rom); one.write(0x44); delay(500); one.reset(); one.select(rom); one.write(0xBE); one.read_bytes(resp, 9); } float getTemp(char unit, int precision) { byte MSB = resp[1]; byte LSB = resp[0]; float celcius; float tempRead = ((MSB << 8) | LSB); //using two's compliment switch (precision) { case 12: celcius = tempRead * 0.0625; break; case 9: celcius = tempRead * 0.25; break; } if (unit == 'F') { return (celcius * 1.8) + 32; } else { return celcius; } }
Wind direction
The wind direction sensor consists of eight magnetically actuated reed switches mounted radially on a PCB at 22.5 degree intervals. Each reed switch is connected between the data line and a DS2401 Silicon Serial number, which provides an identification number for each switch. A rectangular activating magnet polarized with a single pole facing the reed switch, is mounted in a rotor attached to the weather vane axle. The rotor is designed so that one layout can be used for both the wind speed and direction sensors. When used for wind direction, a single magnet is mounted in either one of the two holes located near the center. As the wind rotates the vane and attached rotor, the magnet closes the switch as it passes over the reed. When a reed is closed, it’s companion DS2401 is connected to the bus (if the DS2407 Addressable Switch is on) and the master can read its serial number. This unique serial number identifies the reed switch, and the compass point it represents. Notice that the bus master can only read wind direction information when the DS2407 addressable switch is closed. This is for isolation reasons, otherwise, communication would be disrupted each time a reed switch closed and the DS2401 associated with it signaled its presence on the line. Communication with the wind direction sensor therefore begins when the bus master turns on the DS2407 output, connecting one side of all the DS2401s to the 1-Wire bus ground line. The weather vane with its rotor and magnet will be activating (closing) at least one of the reed switches connecting the other side of that DS2401 to the data line so the bus master can read its serial number. In the code is defined which compass point each DS2401 identifies, it knows which direction the vane is pointing when a particular DS2401 is on the bus. The eight reed switches directly indicate eight compass points. However, the length of the magnet is such that whenever it is approximately half way between two adjacent reed switches both are closed. The bus master understands that this means the weather vane is midway between two compass points, so eight additional directions are inferred, for a total of sixteen compass points. Unfortunately this is currently not implemented in the software.
The DS2407 is switched on and of in an alternating way by the find devices routine. When it is on the DS2401 which are found (maximal 2) are added to the sensor array, as can be seen in the code above.
void setSwitch(uint8_t *rom, bool on) { one.reset(); one.select(rom); one.write(0x55); one.write(0x07); one.write(0x00); if (on) { one.write(0x1F); // 1F direction on, 5F direction off } else { one.write(0x5F); // 1F direction on, 5F direction off } one.write(0xFF); one.write(0xFF); one.read_bytes(resp, 6); one.reset(); }
Wind Speed
Similarly, two magnets mounted on a second rotor attached to the wind cups axle operate a reed switch connected to the DS2423 counter chip which provides a unique identification for this sensor with its 1-Wire serial number. One magnet is mounted in each of the two outermost holes of the rotor. This provides two counts per revolution which improves response at low wind speeds. It also provides rotational balance to the rotor, which becomes important with increasing wind speed, as the rotor can reach 2400 rpm in 160 km/h winds. The DS2423 keeps track of the total number of revolutions the wind cups make and provides the data to the bus master on demand. The chip contains two 232 bit counters and can be powered for ten years with a small Lithium battery, however, here power for the counter chip comes from CR1 and C1 (Figure 1 again) which form a half wave rectifier that “steals” power from the data line. The DS2423 can only be reset to zero when this “parasite power” is lost. Wind speed is calculated by the bus master taking the difference between two counts of the counter used. One count generated before and the other after a clocked interval. The output is currently given in rpm. This later needs to be converted to m/s or km/h.
uint32_t readCounter(uint8_t *rom, uint8_t counter) { uint8_t myTA1; one.reset(); one.select(rom); one.write(0xA5); if (counter == 1) { myTA1 = 0xC0; } else if (counter == 2) { myTA1 = 0xE0; } one.write(myTA1); one.write(0x01); one.read_bytes(resp, 38); one.reset(); uint32_t count = (uint32_t)resp[35]; for (int j = 34; j >= 32; j--) { count = (count << 8) + (uint32_t)resp[j]; } return count; }
Now all sensor data is placed in the sensor array, we can put them into spark variables for distribution in the cloud this is done in getValues:
void getValues() { for(int i=0;i<NUM_SENSORS;i++) { if (sensors[i].id != NULL) { String strid(sensors[i].id); if (strid == "10c1613c000000d5") { temperature = sensors[i].value; } if (strid == "22e8300300000011") { temperature1 = sensors[i].value; } if (strid == "224a3f030000004c") { temperature2 = sensors[i].value; } if (strid == "1d9b1101000000b4") { windspeed = sensors[i].value; } if (strid == "017d384a04000030") { winddir = 0; } if (strid == "018c384a04000075") { winddir = 1; } if (strid == "0174384a040000a6") { winddir = 2; } if (strid == "0177384a040000ff") { winddir = 3; } if (strid == "017a384a040000b5") { winddir = 4; } if (strid == "016b384a040000d9") { winddir = 5; } if (strid == "0183384a04000051") { winddir = 6; } if (strid == "0180384a04000008") { winddir = 7; } } } }
Note that the unique addresses are hard coded in this function. This is done for better understanding of the software. In a practical application its better to put these in defines in the upper part of the code, or in a separate header file.
findDevices and getValues are called from the main loop. For testing we now have a delay of 10 seconds, but for the real application this can be much longer, as the minimal probing time from the cloud service (Atomiot) is 5 minutes, but half an hour is much more appropriate. Eventualy a average over this time span can be calculated, but since we want to run the Core on solar power it's better to put it into sleep for a long period.
void loop() { Serial.println("Spark One-wire weatherstation"); Serial.println("waiting 10 seconds..."); delay(10000); findDevices(); getValues(); }
After some cleaning of the code I will put the full program listing on github, so please stay tuned.
Next time I will have a look at the hardware of the solar power and battery charger.
Don't hesitate when you have some questions.
Top Comments