There is a large body of knowledge on working with 433Mhz weather sensors with Arduinos so that is why this project started out as an Arduino project. But it turned into a Photon project when I realized I didn't want to go through the learning curve of configuring WiFi connectivity with an Arduino. Furthermore the Photon has more processing power so that meant I didn't have to be as concerned about code optimizations. The additional power came in very handy when I started using making extensive use of Serial.print statements. The Arduino was not able to keep up with that level of serial output where the Photon handled it well.
Below is the end result of what gets posted. For Weather Underground only one sensor is posted but for Thing Speak I'm posting the data for three different sensors that are located in different areas of my home. Weather Underground provides much more than what I'm showing in this screen print and has a huge ecosystem for setting up your own personal weather station. You could just buy a pre-built weather station and configure it to post to Weather Undergound but you won't learn as much about IOT if you do that: Personal Weather Station Network.
ThingSpeak also has an Android App which is awesome. This app has ended up being my primary way to view the data as it's so convenient.
I've owned a Lacross WS8610 weather station for years. It can work with up to three different remote units (TX7NU). I also bought a WS8610 for my parents and set them up with two remote units. My goal was to find a way to read the 433Mhz temp and humidity broadcasts from multiple remote sensors and post them to various data collection websites and then geek out with the weather data.
Capturing and analyzing 433mhz transmissionsThe first step was to start making sense of the transmissions from the 433 mhz weather sensors. This involved using a USB to 3.5 mm audio adapter as a means to send the signal being picked up by the 433mhz receiver to the PC microphone input. That signal could then be viewed using Audacity which functioned as a very crude oscilloscope. This gave an initial view of when the transmissions were occurring for each of the three TX7U sensors. Each one would send a broadcast about every 57 seconds.
Here is details on how to hack up a USB to 3.5 mm audio adapter so it becomes the input into Audacity and sends in whatever the 433Mhx receiver picks up..
I learned about the above technique from Cheapest ever 433 Mhz transceiver for PCs. (Note this this link sometimes takes awhile to load).
And here is what the transmission from the sensor looks like in Audacity.
I started a thread on the Arduino forms which goes into detail on my research to decode the signal into the temp and humidity: Read 433Mhz weather sensor - Lacrosse TX7NU. There are additional links in the thread providing detail on the encoding schemes used by Lacrosse weather remote sensors.
Here an analysis spreadsheet I used for decoding of the bits:
The software needs to be pretty smart to know how to watch for the transmission and decode that transmission into the bits that fit the expected pattern.
I think the transmission can sometimes become corrupted by what I'm guessing is general radio frequency noise. Sometimes when watching what is coming over the 433Mhz receiver I'd see other pulses coming in that were not matching up with what the TX7Us were sending. Perhaps there are other devices in my neighborhood that are sending signals on the same frequency. In some cases two TX7U sensors would end up sending their broadcast at close to the same which become impossible for the software to decipher.
I'm using a RXB6 RF 433Mhz receiver. I'm not sure if this is a best in class receiver out there and my from experience the walls and floors in my home seemed to limit it's range. I ended up placing the TX7U sensors fairly close to the Photon receiver or made sure there were as few obstructions as possible between the two devices. I found that creating an antenna of the proper length helped (Make a Simple 433MHz Antenna for Long Range Use) improve the success rate of decoding the transmissions.
While I was making progress on decoding of the signals I was hitting a road block on getting those signals into the micro controller for decoding. I was hoping to use one of the existing 433mhz libraries that would make it easier to accomplish this task: VirtualWire, RadioHead, and RCSwitch. But I could not able to make any real progress in using these libraries.
Then a miracle happened. Or at least what seemed like a miracle. I discovered a far more skilled enthusiast, Emanuele Iannone, who had solved the very problem I was stuck on. He coded from scratch a library which could read the 433mhz Lacrosse weather sensor transmissions! https://github.com/eiannone/WS8610Receiver
This was an amazing stroke of luck as it would have ages for me to understand how to do this on my own and I might have just given up on the project.
The codeThe main engine of my code, TX7UReceiver.ino, is Emanuele's code. From his starting point I added methods 1) to provide ability to configure readings for multiple weather sensors, 2) calculate the dew point, and 3) post to both Weather Underground and ThingSpeak. The Particle cloud methods were added to as needed to interact with the Particle cloud: Paritcle.publish, Particle.variable, Particle.subscribe. After figuring out how the engine worked I added lots of comments and renamed variables to make the code easier for the 'future me' looking at this a year from now to understand. Emanuele's code is using a rolling buffer and lots of low level bit shifting wizardry to decode the analog signal into the sequence of bits that represents the temperature and humidity readings. Wish I could say it was easy to grasp how that wizardry worked but once I did I wanted to write it all down so it would not slip away.
Weather Underground APIHere is an example of what that the Weather Underground post looked like:
Refer to the Weather Underground 'PWS - Upload Protocol' for the specifications for this post.
You'll need to register a Personal Weather Station to get a Station ID and a Station Key: Personal Weather Station Network.
I then used the HttpClient Particle Library to perform this post. I initially tried using the TCPClient, which is built into the Particle Firmware, but could not get it working.
ThingSpeak APIYou'll need to sign up and create a ThingSpeak account and then configure a Channel. There are eight data fields per Channel in Thingspeak so that meant it was possible to record up to eight separate data points. I have three TX7U sensors and each one had a temp and humidity so that is six datapoints total.
Here is the documentation for a ThingSpeak Get: Write Data with a Get.
I used the TCPClient to peform GET update. It ended up looking something like this:
Sensor IDsEach TX7U sensor has a two digit Id that is randomly created when the battery replaced. I'm not exactly sure how this assignment is orchestrated but this ID is needed otherwise it will not be possible to associate what reading is coming from what sensor.
In my code there is a string variable 'lastIdsRead' that contains a rolling string of all the successful decodings. One way to view the current contents of this string is to hook up the Photon to the USB serial cable and run the command that displays that variable for the connected Photon:
C:\Users\joe>particle variable get joesphoton4 lastIdsRead
#54 64.220001 F,#54 55 rh,#53 66.019997 F,#53 66.019997 F,#53 53 rh,#53 53 rh,#81 86.000000 F,#81 86.000000 F,#81 33 rh,#54 64.220001 F,#54 64.220001 F,#54 54 rh,#54 54 rh,#53 66.019997 F,#53 66.019997 F,#53 53 rh,#81 85.820000 F,#81 32 rh,#54 64.220001 F,#54 64.220001 F,#54 54 rh,#53 66.199997 F,#53 66.199997 F,#53 52 rh,#81 85.820000 F,
#54, #53, #81 are the three device Ids for the TX7Us. The temp (F) and humidity (rh) values following each ID. If the battery is replaced on one of the sensors then the ID would change and I'd have to update the *.ino code (see below) for that ID.
Since 'lastIdsRead' is a particle variable it can also be accessed through the Particle cloud console. This is the easiest way to get that info without going through the bother of connecting the Photon to a serial USB port.
DebuggingThe other reason for using the console is to view all the Serial.println detail of what is going on during the program execution. This detail can only be viewed when the Photon is connected to your machine via USB. This output is not meaningful until you have a handle on what the code is doing.
C:\Users\joe>particle serial monitor
Opening serial monitor for com port: "COM3"
Serial monitor opened successfully:
................pulsePos: 81, delay:29020
[0]1010 [1]110 [2]10110110 [3]10010001 [4]1101001 [5]1010
#53: 66.38 °F
_outdoorSensorId=53, sensorAddress=53, farenheit=66.380005, humidity=52, _wuSend 1
pulsePos: 81, delay:315534
[0]1010 [1]110 [2]10110110 [3]10010001 [4]1101001 [5]1010
#53: 66.38 °F
_outdoorSensorId=53, sensorAddress=53, farenheit=66.380005, humidity=52, _wuSend 1
pulsePos: 87, delay:28749
[0]1010 [1]11100110 [2]10110101 [3]100000 [4]1010010 [5]111
#53: 52.00 %rh
Getting the highest success rate with the decoding involved allot of trial and error tweaking of the various micro second level intervals used by the decoding logic:
// Needed to tweak these intervals in order to get more consistent readings.
#define PW_FIXED 1000 // Pulse width for the "fixed" part of signal
#define PW_SHORT 550 // Pulse width for the "short" part of signal(1 bit)
#define PW_LONG 1350 // Pulse width for the "long" part of signal (0 bit)
#define PW_TOLERANCE 300 // plus/minus range for valid FIXED, SHORT and LONG pulses
#define SIZE_BUFFER 88 // two pulses needed to determine each bit
#define SIZE_PACKETS 6 // each packet is 8 bits
The setting which had the most impact on obtaining better decodings was the PW_TOLERERANCE. The only way to perform this tweaking was to watch the output from the console to see how often a pulse was able to be decoded.
Crashing of PhotonOne of the most aggravating issues was the Photon going into red flash 'SOS' mode. I did not have any real clue as to why. After hours of trial and error it turned out one of the culprits was a Serial.printlf statement that was doing something 'bad'. Here is one of the offenders that I ended up commenting out.
//WARNING! THIS printlnf KILLS PHOTON AND I FRIGGEN DON'T KNOW WHY! FLASHING RED LIGHT
//Serial.printlnf("sendWUData> station=%s, password=%s, sensor=%i, farenheit=%f, humidity=%i",
// _wuStationId.c_str(), _wuPassword.c_str(), reading->sensorId, reading->farenheit, reading->humidity);
http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol
I put several dozen Serial.print statements all over the code and which are probably creating all sorts of havoc that I don't know about. But this it was the only way I knew of to get insight into what was going on while the code was executing.
ConfigurationI ended up with an approach that relied upon hard coding across an enum, a method, and a switch statement. Kind of ugly but this is the best I knew how to do with my limited C+ experience. The configuration associates a Photon cloud-aware device name to an enum. That enum is then used in a switch statement that 1) associates that Photon with it's Weather Underground and ThingSpeak credentials, 2) maps the ThingSpeak fields to the temp or dew point reading for a particular sensor, and 3) identifies which TX7U sensor ID is the outdoor sensor. The outdoor sensor's readings are then sent to Weather Underground. You'll need to update this configuration with the name of your Photon, your ThingSpeak Channel Number, ThingSpeak API Key, Weather Underground Station Id, and Weather Underground password.
// enum used by code
enum PhotonNames
{
eDadPhoton1,
ejoesPhoton,
ejoesPhoton3,
ejoesPhoton4,
eNameNotFound
};
// map cloud
PhotonNames getNameEnum(std::string const &inString)
{
std::string lower = toLowerx(inString);
if (lower == "dadphoton1")
return eDadPhoton1;
if (lower == "joesphoton")
return ejoesPhoton;
if (lower == "joesphoton3")
return ejoesPhoton3;
if (lower == "joesphoton4")
return ejoesPhoton4;
return eNameNotFound;
}
// Configurations
switch (getNameEnum(_enumCrap))
{
case eDadPhoton1:
Serial.println("case eDadPhoton1");
_tsChannelNumber = XXXXX;
_tsWriteAPIKey = "XXXXXXX";
_outdoorSensorId = 25;
_wuStationId = "XXXXXX";
_wuPassword = "XXXXX";
_readings[0].sensorId = 25;
_readings[0].location = "outside";
_readings[0].tsTempField = 1;
_readings[0].tsHumidityField = 2;
_readings[1].sensorId = 14;
_readings[1].location = "living room";
_readings[1].tsTempField = 3;
_readings[1].tsHumidityField = 4;
_NumberOfSensors = 2;
break;
case ejoesPhoton3:
Serial.println("case ejoesPhoton3");
_outdoorSensorId = 53;
_tsChannelNumber = XXXXX;
_tsWriteAPIKey = "XXXXXXXXXXXX";
_wuStationId = "XXXXXXXXXX";
_wuPassword = "XXXXXXXX";
_readings[0].sensorId = 18;
_readings[0].location = "attic";
_readings[0].tsTempField = 1;
_readings[0].tsHumidityField = 2;
_readings[1].sensorId = 54;
_readings[1].location = "basement";
_readings[1].tsTempField = 5;
_readings[1].tsHumidityField = 6;
_readings[2].sensorId = 53;
_readings[2].location = "outside";
_readings[2].tsTempField = 3;
_readings[2].tsHumidityField = 4;
_NumberOfSensors = 3;
break;
default:
Serial.printlnf("Photon named %s is not configured!", _enumCrap.c_str());
break;
}
ConclusionI liked how the project relied upon finding help from others to figure things out. The hardware aspect is pretty simple but things got tricky when it came to the software and debugging which is the reason I gave the project an intermediate rating. I also liked how this project gave some 'old school' devices ability to be cloud enabled.
In the future I'm hoping to find a way to improve transmissions being reliably decoded up over greater distances. And maybe add more sensors but this would be dependent upon having the decoding logic for the particular device available.
Comments