Results tagged “Programming” from Bill's Words
I just bought Keyboard Maestro from Stairways Software last week and already it is de-frustrating my co-existance with Microsoft Outlook in meaningful ways. Here are two ways:
Intercept Undesired “Close” Keystrokes
Because I use a two-monitor setup with Outlook over there on that far-off monitor (actually, my MacBook display), I oftentimes switch to Outlook to read a message but forget to switch focus to Safari (or another app) where I might hit ⌘W… and inadvertently close an Outlook “Main Window” (as they’re called).
I instituted the following KM macro to prevent this problem:
It merely looks to see if the current frontmost window contains “Calendar” or the name of my company (Bloomy Controls, Inc.) which shows up in the message viewer window no matter which mailbox is selected, e.g. “Inbox • Bloomy”. This is true except for Smart Folders, which I don’t use. If the frontmost window doesn’t meet these conditions, it sends a ⌘W to Outlook and that window will close.
Sensible Find Shortcuts
I hate Microsoft Outlook’s Find functionality. Other than the fact that it can, indeed, find anything in any folder, the interface to use it is awful. And since I use the Deleted Items folder as my brain, the Find function is very important. Using it out of the box involves pressing ⇧⌘F and then clicking on the “All Items” to un-limit the search scope (every time—why, Microsoft, doesn’t it remember what scope I used before?).
Getting away from Find involves clicking on a red X in a circle called “Close”… unless you actually selected a message to look at, in which case the ribbon (WHICH I HATE) changes state to “Home”. If you did (and why wouldn’t you?), you have to click on the Search portion of the Ribbon selector and then on the red X in a circle.
Keyboard Maestro macro to the rescue!
This one’s a bit more complicated as it has to check to see what state the Outlook window is in. But pressing ⇧⌘F becomes the equivalent of “Search all items,” and pressing it again closes out the search.
(By the way, this one’s not bulletproof. Among other things, if there’s an E-mail window open with the word “Bloomy” in the title—substitute your own text here, of course—it’ll get brought to the front and might cause the rest of the macro to fail.)
I’ll add more macros as I make them.
If you stumbled across this blog looking for information on the WS-2010-13 PC interface, then, man! are you in luck!
A previous entry here shows the code I use with an mbed processor to do the posting to a database on a webserver. If you have a Linux or Windows box, however, the code below might do you a bit of good. If not, then… well, keep hacking!
(Note: I just discovered that Feedbin mangles the code a bit. If you’re getting the code via an RSS reader, you might not be seeing the code in its entirety. Visit the source article for an unadulterated copy of the code.)
#!/usr/bin/perl -w # # WeatherStationInterface.cpp # An interface for the LaCrosse Technology WS-2010-13 PC Interface. # Copyright (C)2012 William N. Eccles # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit # persons to whom the Software is furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be includied in all copies or substiantial portions of the # Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECITON WITH # THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. use Win32::SerialPort; use LWP::UserAgent; # found these items somehwere--maybe it was from the original LCT docs, but I'm not sure #define SOH 0x01 #define STX 0x02 #define ETX 0x03 #define EOT 0x04 #define ENQ 0x05 #define ACK 0x06 #define DLE 0x10 #define DC2 0x12 #define DC3 0x13 #define NAK 0x15 # command:# @commands = ( # "\x01\x30\xcf\x04", # '0' = Poll DCF time # "\x01\x31\xce\x04", # '1' = Request dataset # "\x01\x32\xcd\x04", # '2' = Select next dataset # "\x01\x33\xcc\x04", # '3' = Activate 9 temperature sensors # "\x01\x34\xcb\x04", # '4' = Activate 16 temperature sensors # "\x01\x35\xca\x04", # '5' = Request status # "\x01\x36\x53\xc9\x04" # '6' = Set interval time # ); # some utility routines # return the high nybble of a byte in the low nybble sub H { my( $byte )= $_[0]; return $byte>>4; } # retun an usigned high nybble of a byte in the low nybble sub HS { my( $byte )= $_[0]; return ($byte >> 4) & 0x07; } # return the sign of a byte sub S { my( $byte )= $_[0]; $byte = ($byte & 0x0F) >> 3; return (($byte==1) ? -1 : 1); } # return the low nybble of a byte sub L { my( $byte )= $_[0]; $byte = ($byte & 0x0F); return $byte; } # return an unsigned low nybble of a byte sub LS { my( $byte ) = $_[0]; $byte = ($byte & 0x07); return $byte; } # make the WS2010 sleep. Show a debug message while we're at it. sub gotosleep { print "telling WS2010 to sleep.\n"; $ob->dtr_active(F); return; } # wake up the WS2010. Print lots of debugging stuff while doing it. sub wakeup { # make a low->high transition on DTR to let WS2010 know we're here. $ob->rts_active(No); $ob->dtr_active(F); $ob->dtr_active(T); ($count, $result) = $ob->read(2); $count = length($result); # debug print "Wokeup WS2010 and received ",$count," characters.\n"; print join(" ",unpack("C*",$result)),"\n"; # gubed @return = unpack("C*",$result); # debug if (@return!=1) { warn "Invalid number of characters received on wakeup.\n"; } if ($return[0]!=3) { warn "WS2010 returned invalid wakeup signal. \n"; } else { print "WS2010 is ready.\n"; } # gubed } # # main program # print "Opening serial port...\n"; # for now, we'll assume that some other program (such as WS-2010 PC) has been # used to set the polling interval and number of sensors. We'll write one # that will be used someday. # The problem is that if you do this every time this program is run, it # clears the history stored in the WS2010. I think it's just a matter of # sending command #3 (or #4) and #6. Come to think of it, I don't think # I've ever recorded and examined this transaction. # open the com port $ob = Win32::SerialPort->new ('COM1'); die "Can't open serial port COM1: $^E\n" unless ($ob); # setup the serial port. Die with meaningless message if we don't succeed. $ob->baudrate(9600) || die "Failed to set the baudrate. Dunno' why.\n"; $ob->parity("even") || die "Failed to set the parity. Dunno' why.\n"; $ob->databits(8) || die "Failed to set the databits. Dunno' why.\n"; $ob->stopbits(2) || die "Failed to set the stopbits. Dunno' why.\n"; $ob->handshake("none") || die "Failed to set the handshake.\n"; $ob->write_settings || die "Failed to write settings to port. Dunno' why.\n"; # debug my($baudrate) = $ob->baudrate; my($parity) = $ob->parity; my($databits) = $ob->databits; my($stopbits) = $ob->stopbits; print "$ob opened at $baudrate/$databits/$parity/$stopbits\n"; # gubed $ob->read_interval(100); $ob->read_const_time(5000); # # the big loop # each time through the loop: # wait six minutes (unless it's the first time through this loop) # open the com port # read data and post it until it's all gone # close the com port # # There's enough debugging stuff in here that I won't bother with labeling it. # Suffice it to say, it's all the "print" stuff, none of which is required for # proper operation of the system. $first = 1; while (1) { if (!$first) { print "Sleeping for a long time...\n"; gotosleep(); $i=10; while ($i>=1) { print $i,"...\n"; sleep(120); $i=$i-1; } print "0.\n"; sleep(1); } $first = 0; $haveData = 1; while ($haveData) { # request a data block wakeup(); print "Requesting data.\n"; $ob->write("\x01\x31\xce\x04"); ($count, $result) = $ob->read(100); $count = length($result); print $count," characters returned:\n"; print join(" ",unpack("C*",$result)),"\n"; @return = unpack("C*",$result); if ((@return==0)||($return[0]!=2)||($return[@return-1]!=3)) { warn "Bad dataset received or no data found.\n"; $ob->purge_rx; $haveData = 0; # quit asking for data--give a cooloff time } elsif (($return[1]==1)&($return[2]==16)) { print "No data available.\n"; $haveData = 0; } else { # select the next data block print "Requesting next block.\n"; $ob->write("\x01\x32\xcd\x04"); ($count, $newresult) = $ob->read(5); print $count," characters returned:\n"; print join(" ",unpack("C*",$newresult)),"\n"; @newreturn = unpack("C*",$newresult); if (($newreturn[2]==16)&($newreturn[1]==1)) { print "No dataset ready.\n"; $haveData = 0; # no data to be had } # strip out the ENQ escape sequences and leading STX and length, trailing ETX @stripped = (); for ($i=2; $i<@return-2; $i++) { if ($return[$i]==0x05) { @stripped = (@stripped, $return[$i+1]-0x10); $i++; } else { @stripped = (@stripped, $return[$i]); } } print "Dataset read.\n"; @return = @stripped; print join(" ",@return),"\n"; $db = $return[1]*256+$return[0]; $dt = $return[3]*256+$return[2]; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time-($dt*60)); $year=1900+$year; $mon++; printf "dataset %06d %06d %04d/%02d/%02d:%02d%02d\n",$db,$dt,$year,$mon,$mday,$hour,$min; $getstr=sprintf("http://example.com/yourQuery.php?loc=12345&dataset=%06d&datetime=%04d-%02d-%02d%%20%02d:%02d:00",$db,$year,$mon,$mday,$hour,$min); ($i, $i, $i, @return) = @return; # I know, there's probably a better way to do it... strip off header info. # each reading consists of the reading itself and whether or not it's a new or old value. # variable names are somewhat consistent with this. $t1c = (LS($return[2])*10+H($return[1])+L($return[1])/10)*S(L($return[2])); $t1f = 9/5*$t1c+32; $h1 = LS($return[3])*16+H($return[2]); $n1 = (S(L($return[3]))==-1) ? "NEW" : "OLD"; if ($t1f>70) { $hif = -42.379+2.04901523*$t1f+10.14333127*$h1-0.22475541*$t1f*$h1- ((6.83783e-3)*$t1f*$t1f)-((5.481717e-2)*$h1*$h1)+ ((1.22874e-3)*$t1f*$t1f*$h1)+((8.5282e-4)*$t1f*$h1*$h1)- ((1.99e-6)*$t1f*$t1f*$h1*$h1); $hic = ($hif-32)*5/9; } else { $hif = $t1f; $hic = $t1c; } $getstr .= "&t1=$t1f&h1=$h1&n1=$n1&hif=$hif&hic=$hic"; # print "S1: $t1f∫F $h1% $n1\n"; print "HI: $hif∫F $hic∫C\n"; $tic = (LS($return[29])*10+H($return[28])+L($return[28])/10)*S(L($return[29])); $tif = 9/5*$tic+32; $hi = LS($return[30])*16+H($return[29]); $ni = (S(L($return[30]))==-1) ? "NEW" : "OLD"; $getstr .= "&ti=$tif&hi=$hi&ni=$ni"; # print "IN: $tif∫F $hi% $ni\n"; $sp = (HS($return[24])*100+L($return[24])*10+H($return[23])+L($return[23])/10)*.6215; $dir = (L($return[26])%4)*100+H($return[25])*10+L($return[25]); $spreadraw = int(L($return[26])/4); if ($spreadraw==1) { $spread = 22.5; } elsif ($spreadraw==2) { $spread = 45; } elsif ($spreadraw==3) { $spread = 67.5; } else { $spread = 0; } $nwin = (S(H($return[24]))==-1) ? "NEW" : "OLD"; $getstr .= "&sp=$sp&dir=$dir&spread=$spread&nwin=$nwin"; # print "WIN: $sp mph at $dir +/- $spread $nwin\n"; if (($t1f<=50)&($sp>3)) { $wc = 35.74+(0.6215*$t1f)-(35.75*($sp**0.16))+(0.4275*$t1f*($sp**0.16)); } else { $wc = $t1f; } $getstr .= "&wc=$wc"; $pr = (H($return[27])*100+L($return[27])*10+H($return[26])+200)/33.775; $getstr .= "&pr=$pr"; # print "PR: $pr hPa\n"; # 0.0145636 in/count # rain is a tricky thing. The rain sensor merely counts tips of the seesaw and reports # that number of counts periodically. It's a 12 bit counter, so there's a challenge # in the database/reporting program to discover that it's rolled over and has not # reset through a battery change or fluke. What I've done is determined that # if the count goes from "high" to "low", where high is currently set to >4080, and # low is <20, then there was, indeed, a rollover and not a reset. I also store both # the raw value reported by the rain gage as well as the delta from the last reading # so that a simple SUM query can be done to get the total rainfall during a given # period of time. $rn = (($return[22]&0x7F)*256+$return[21]); $nr = (S(H($return[22]))==-1) ? "NEW" : "OLD"; $getstr .= "&rn=$rn&nr=$nr"; $ua = new LWP::UserAgent; $ua->agent("EcclesAgent/0.1 ".$ua->agent); $httpError = 1; while ($httpError) { print $getstr,"\n"; my $req = new HTTP::Request GET => $getstr; # $req->content_type('application/x-www-form-urlencoded'); # $req->content('match=www&errors=0'); my $res = $ua->request($req); if ($res->is_success) { print $res->content; $httpError = 0; } else { print "HTTP Error\nWaiting to try again...\n"; $i=10; while ($i>=1) { print $i,"...\n"; sleep(1); $i=$i-1; } print "0.\n"; sleep(1); } } undef $req; undef $ua; # gotosleep(); sleep(1); } # end of massive if statement # print "Requesting status.\n"; # wakeup(); # $ob->write("\x01\x35\xca\x04"); # ($count, $result) = $ob->read(100); # $count = length($result); # print $count," characters returned:\n"; # print join(" ",unpack("C*",$result)),"\n"; } # end of while ($havedata) } # end of while (1) undef $ob; # close serial port
We have a LaCrosse Technology WS-2010 system which is getting close to being a decade old at this point. Aside from what I don’t like about the system (and there’s a lot), it has performed reliably, sucking weather data out of the atmosphere. Being a Mac-centric family, the fact that the WS-2010-13 PC Interface is a Windows-only, RS-232-based device was maddening. And the fact that the included software didn’t do anything useful for me didn’t help.
So for the last decade, I’ve had a Windows PC hooked up to it. I wrote a Perl script which grabbed the data from the interface and posted it to a MySQL database on a local Mac OS X Server. PHP scripts on that server query the database and generate all kinds of good graphs, some of which I’m particularly proud.
But that PC has become incredibly unreliable, requiring a reset four to five times per week. Sometimes the hard drive required unsticking with a firm whack. (I have a special tool for it, a dead TPV—a solid chunk of brass—from the nearby boiler.) Over the Christmas break, I decided to replace the PC with an mbed microcontroller. Unlike a lot of other microcontrollers out there, this one is very friendly with Ethernet, and that was extremely important in choosing it for the solution to this project. Its price, $59, isn’t too bad, and all I had to do was add a bit of hardware to talk RS-232, rewrite my Perl script as C++, and I’d be done.
As they say, How hard could it be?
I’ve learned a lot in the past two weeks. (Yes, I had two weeks off—vacation plus company-sponsored break.) First, I learned that the resources I relied upon a decade ago to write the code the first time have dried up. Google finds all kinds of references to 2010 (which arrived two years ago, you know), and a lot of them include “WS” in them. Depending on the memory of the Internet for useful things isn’t necessarily a good idea. (Depending on the memory of the Internet for publicly embarrassing things is probably a whole ‘nother story.) Here it is so I at least have a copy, and eventually Google will, too.
Second, I learned a lot about the WS-2010-13 hardware. The manual says specifically that operating it requires “RTS <-9V and DTR >+9V”. I thought that was crap because there are only four wires from the DE-9 connector. I reasoned that one must be TxD, one must be RxD, one must be DTR (because it relies on a transition of DTR to know when to wakeup), and the last must be ground, right? Wrong. Indeed, the last wire is RTS and there is no ground that I can find. How this works exactly, I don’t know, but it does. (I have some guesses, but they’re not worth repeating here.) Anyway, it’s acceptable to connect RTS straight to -12VDC, but RxD, TxD and DTR must use level shifters to connect to your processor.
Third, I re-learned that “high” in RS-232 is -12VDC (actually, between -3VDC and -25VDC) and “low” is +12VDC (+3VDC to +25VDC, of course), so that when you wake up the WS-2010, you should really follow the instructions of the manual and wake it up with a high-low transition on RTS which leaves the RTS at +12VDC. I spent a lot of time waking the interface up with a -12V/+12V/-12V sequence, which woke the interface up, producing the correct response, but which then put it right back to sleep again.
Fourth, I learned the hard way that even after everything works with clipleads, breadboards, and whatnot, you can still royally foul things up when you solder everything in place by reversing VEE and VCC on your MC1488P. The 1488 will let out a little crackle of sorts and will rapidly cease to function. Even after restoring the power in the correct orientation, it will act much like a low-resistance path between VEE and VCC and the 1488 will get pretty toasty. Don’t repeat my mistake.
As to the subtleties of getting the WS-2010-13 to work, it’s not really all that difficult. Most of the info you need is in the manual, the two exceptions being the interpretation of the wind direction spread and rain gauge return. The former is pretty simple, but the latter presents some problems occasionally. The rain gauge, as I’m sure you’re aware, is just a see-saw mechanism. Each tip of the mechanism represents 0.0145636” of rainfall. (I found that somewhere online a decade ago, and now I can’t find it at all.) A 12-bit counter in the rain gauge reports back the total number of tips it’s ever counted. Where things get a bit tricky is that occasionally it reports back a number where some high-order bit has flipped, and then a while later, it’ll flip back. I.e., you’ll have a sudden downpour of 80” of rain and then it’ll dry up just as suddenly, or vice-versa. I choose to report back the raw count coming out of the counter and let the server figure out what happened since it can “look back in time” to see which might be realistic.
If you’d like to see what the data look like, I report the weather here.
The code to do this with an mbed is shown below, and should be available on the mbed website soon. Note that there’s an awful lot of debugging feedback code commented out. These lines are quite useful when things are going wrong, believe you me! But since transmitting the serial data takes a while (even via USB), things run much faster when it’s not needed and is commented out. That I know of, you can uncomment anything and it won’t affect the timing of the conversation with the WS-2010-13. You should also be able to move this to just about any processor as the mbed-specific code is pretty easy to read as pseudo-code and you can substitute your own subroutine calls.
I hope this turns out to be of some use to somebody out there. If it does, please let me know.
/* WeatherStationInterface.cpp An interface for the LaCrosse Technology WS-2010-13 PC Interface. Copyright (C)2012 William N. Eccles Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be includied in all copies or substiantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECITON WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "mbed.h" #include "EthernetNetIf.h" #include "HTTPClient.h" #define WAIT_time 0.05 #define D() wait(WAIT_time) Serial mac(USBTX, USBRX); Serial ws2010pc(p28, p27); // tx, rx DigitalOut DTR(p29); DigitalOut mled0(LED1); DigitalOut mled1(LED2); DigitalOut mled2(LED3); DigitalOut mled3(LED4); const char *requestDCFtime = "\x01\x30\xCF\x04"; const char *requestData = "\x01\x31\xCE\x04"; const char *requestNextDataset = "\x01\x32\xCD\x04"; const char *requestStatus = "\x01\x35\xCA\x04"; void blink() { int m0, m1, m2, m3; m0 = mled0; m1 = mled1; m2 = mled2; m3 = mled3; mled0 = mled1 = mled2 = mled3 = 0; mled0 = 1;D();mled1 = 1;D();mled2 = 1;D();mled3 = 1;D(); mled0 = 0;D();mled1 = 0;D();mled2 = 0;D();mled3 = 0;D();D();D(); mled3 = 1;D();mled2 = 1;D();mled1 = 1;D();mled0 = 1;D(); mled3 = 0;D();mled2 = 0;D();mled1 = 0;D();mled0 = 0;D();D();D(); mled0 = m0; mled1 = m1; mled2 = m2; mled3 = m3; // takes 1s with a delay time of 0.05s return; } int errorHalt( bool L3, bool L2, bool L1, bool L0 ) { while (1) { mled3 = L0; mled2 = L1; mled1 = L2; mled0 = L3; wait(0.5); mled3 = false; mled2 = false; mled1 = false; mled0 = false; wait(0.5); } return -1; } void showLEDs( bool L3, bool L2, bool L1, bool L0 ) { mled3 = L0; mled2 = L1; mled1 = L2; mled0 = L3; return; } float H( int byte ) { return (float)(byte >> 4); } float HS( int byte ) { return (float)((byte >> 4) & 0x07); } float S( int byte ) { byte = (byte & 0x0F) >> 3; return ((byte==1) ? -1.0 : 1.0); } float L( int byte ) { byte = (byte & 0x0F); return (float)byte; } float LS( int byte ) { byte = (byte & 0x07); return (float)byte; } bool wakeup() { char aChar; bool wokeup = false; // mac.printf("Waking up the WS2010..."); DTR = 1; wait(0.05); DTR = 0; wait(0.05); while (ws2010pc.readable()) { aChar = ws2010pc.getc(); if (aChar==3) { // mac.printf(" and it's awake.\n\r"); wokeup = true; } else { // mac.printf(" and it didn't want to wake up, returning a %i instead of a 3.\n\r",aChar); wokeup = false; } } wait(0.1); return wokeup; } int sendString( char *theString ) { int index = 0; while (theString[index]!='\0') { ws2010pc.putc((char)theString[index++]); // wait(0.005); } return index; } int receiveString( char *theString, int numChars, int timeout_ms ) { int index = 0; bool done = false; int LSRValue; Timer aTimer; theString[0]='\0'; aTimer.start(); while ((aTimer.read_ms()<=timeout_ms)&&(indexLSR; // if ((LSRValue!=97)&&(LSRValue!=96)) { mac.printf("Receive error? LSR is %i.\n\r",LSRValue); } if (ws2010pc.readable()) { theString[index] = ws2010pc.getc(); if (theString[index]==0x03) { done = true; } index++; theString[index]='\0'; } } aTimer.stop(); return index; } // MAIN int main() { int i, j, dataLength, strippedLength, LSRValue; int db, dt, spreadraw; float t1c, t1f, h1, hif, hic; float tic, tif, hi; float sp, dir, spread, wc; float pr; float rn; string n1, ni, nwin, nr; HTTPText txt; HTTPResult r; bool error, firstTime, haveData, dataPostedOK; char theData[1024], strippedData[1024], httpQuery[1024]; EthernetNetIf eth; HTTPClient http; EthernetErr ethErr; IpAddr myIpAddr; firstTime = true; // Setup Ethernet showLEDs( false, false, false, true ); // mac.printf("Setting up Ethernet...\n\r"); ethErr = eth.setup(); if (ethErr) { // mac.printf("Error %d in setup.\n\r", ethErr); errorHalt( false, false, false, true ); } else { // mac.printf("Ethernet setup was successful.\n\r"); } while (1) { error = false; // Setup serial port showLEDs( false, false, true, false ); // mac.printf("Opening serial port...\n\r"); ws2010pc.baud(9600); ws2010pc.format(8, Serial::Even, 2); // Wait six minutes unless this is the first time through this showLEDs( false, false, true, true ); if (!firstTime) { DTR = 1; mac.printf("Sleeping for six minutes... "); i=6; while (i>=1) { mac.printf("%i...",i--); for (j=0; j<12; j++) { blink(); wait(4); } } mac.printf("\n\r"); } firstTime = false; // Wakeup the 2010 showLEDs( false, true, false, false ); error = !wakeup(); // Ask the 2010 for data until there is no more to be had. haveData = true; while (haveData&&(!error)) { showLEDs( false, true, false, true ); mac.printf("Requesting data.... "); dataLength = sendString((char *)requestData); dataLength = receiveString(theData, 128, 500); mac.printf("Received %i characters.\n\r",dataLength); if (dataLength<2) { mac.printf("No data received.\n\r"); haveData=false; } else if ((theData[0]!=2)||(theData[dataLength-1]!=3)) { mac.printf("Bad dataset received.\n\r"); haveData = false; } else if ((theData[1]==1)&&(theData[2]==16)) { mac.printf("No data available at the moment. Will retry later.\n\r"); haveData = false; } if (haveData) { /* mac.printf("The raw dataset has %i characters: ",dataLength); i=0; while (i 70) { hif = -42.379+2.04901523*t1f+10.14333127*h1-0.22475541*t1f*h1-((6.83783e-3)*t1f*t1f)-((5.481717e-2)*h1*h1)+((1.22874e-3)*t1f*t1f*h1)+((8.5282e-4)*t1f*h1*h1)-((1.99e-6)*t1f*t1f*h1*h1); hic = (hif-32.0)*5.0/9.0; } else { hif = t1f; hic = t1c; } // mac.printf(" T1C:%5.2f T1F:%5.2f H1:%5.2f HIF:%5.2f HIC:%5.2f %s \n\r", t1c, t1f, h1, hif, hic, n1); // reading: indoor temperature and humidity tic = (LS(strippedData[29])*10+H(strippedData[28])+L(strippedData[28])/10)*S(L(strippedData[29])); tif = 9.0/5.0*tic+32; hi = LS(strippedData[30])*16+H(strippedData[29]); ni = (S((int)L(strippedData[30]))==-1) ? "NEW" : "OLD"; // mac.printf(" TIC:%3.2f TIF:%3.2f HI:%3.2f %s \n\r", tic, tif, hi, ni); // reading: wind speed, direction and directional spread, wind chill sp = (HS(strippedData[24])*100.0+L(strippedData[24])*10.0+H(strippedData[23])+L(strippedData[23])/10.0)*0.6215; dir = (float)((int)L(strippedData[26])%4)*100.0+H(strippedData[25])*10.0+L(strippedData[25]); spreadraw = (int)(L(strippedData[26])/4); switch (spreadraw) { case 1: spread = 22.5; break; case 2: spread = 45; break; case 3: spread = 67.5; break; default: spread = 0; } nwin = (S((int)H(strippedData[24]))==-1) ? "NEW" : "OLD"; // mac.printf(" SP: %5.2f DIR:%5.2f SPREADRAW:%i SPREAD:%5.2f %s \n\r", sp, dir, spreadraw, spread, nwin); if ((t1f<=50)&&(sp>3)) { wc = 35.74+(0.6215*t1f)-(35.75*pow(sp,(float)0.16))+(0.4275*t1f*pow(sp,(float)0.16)); } else { wc = t1f; } // mac.printf(" WC: %5.2f\n\r",wc); // reading: air pressure pr = (H(strippedData[27])*100.0+L(strippedData[27])*10.0+H(strippedData[26])+200.0)/33.775; // mac.printf(" PR: %6.3f\n\r",pr); // Ah, rain. The see-saw tips, and each tip represents 0.0145636 inches of rain. rn = (strippedData[22]&0x7F)*256+strippedData[21]; nr = (S((int)H(strippedData[22]))==-1) ? "NEW" : "OLD"; // mac.printf(" RN: %7.1f NR %s\n\r",rn,nr); // Generate the query string j = sprintf(httpQuery, "http://www.example.com/yourquery.php?datasetnumber=%06d&timedelta=%06d&t1=%.1f&h1=%.1f&n1=%s&hif=%.1f&hic=%.1f&ti=%.1f&hi=%.1f&ni=%s&sp=%.1f&dir=%.1f&spread=%.1f&nwin=%s&wc=%.1f&pr=%.7f&rn=%.1f&nr=%s",db,dt,t1f,h1,n1,hif,hic,tif,hi,ni,sp,dir,spread,nwin,wc,pr,rn,nr); // mac.printf("The query is \"%s\"\n\r",httpQuery); // Now try and post the data. Make sure the response from the server doesn't exceed capacity of the buffer "txt". showLEDs( false, true, true, false ); r = http.get(httpQuery, &txt); if(r==HTTP_OK) { // mac.printf("HTTP result:\"%s\"\n\r", txt.gets()); dataPostedOK = true; } else { // mac.printf("HTTP error #%d\n\r", r); dataPostedOK = false; } if (dataPostedOK) { showLEDs( false, true, true, true ); error = !wakeup(); mac.printf("Requesting next dataset.... "); dataLength = sendString((char *)requestNextDataset); dataLength = receiveString(theData, 128, 300); mac.printf("Received %i characters.\n\r",dataLength); if (dataLength<2) { mac.printf("No data received.\n\r"); haveData=false; error = true; } else if ((theData[0]!=2)||(theData[dataLength-1]!=3)) { mac.printf("Bad dataset received.\n\r"); haveData = false; error = true; } else if ((theData[1]==1)&&(theData[2]==16)) { mac.printf("No data available at the moment. Will retry later.\n\r"); haveData = false; } } } // end of "if (haveData)" } } errorHalt( true, true, true, true ); return 0; }
Here’s what I posted a few moments ago on stackoverflow.com, a great site for getting clues regarding programming problems from the very esoteric to the very, ahem, basic:
OK, knowledgeable programmer-types, please be gentle…
I’m having trouble getting a very simple, one-view “Hello World” app to rotate automatically. I go through the usual “Hello World” steps:
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation {
// Return YES for supported orientations
return YES;
}
*/
(Um… Duh. I just saw it. But I will continue with my story for your amusement.)
Frustration set in after about an hour of Googling for the answer. Everywhere I looked, each source said the same thing: Just change the return value to “yes”. And yet…
I even stuck debugger breakpoints in! None of them were hit! (Shock, surprise!) Figuring that I didn’t know how to use the debugger, I stuck a breakpoint in on “initWithNibName.” Guess what?! That didn’t get hit, either! What the… heck?!
So I downloaded Apple’s WhichWayIsUp app to test my breakpoint l33t breakpoint skilz. Sure enough, it beeps away as I rotate the “iPhone” around. So what am I doing wrong?!
Surely, I’m not that ignorant that I… no, wait, I take that back. If you saw my error, above, then you are certainly thinking, “Yes, he is that ignorant.” And you’re right.
For those of you who stumbled across this question, are similarly frustrated, and didn’t see the problem yet, here’s the answer: the entire shouldAutorotateToInterfaceOrientation method is encapsulated by “/* */” comment tokens. It never got compiled, much less called.
Now, that I know of, none of the sources that I found make it obvious that you have to ensure that the code block is uncommented. Several say, “All you have to do is change exactly one line of code.” And, except for those other two with the comment tokens on them, they’re exactly right.
If I just helped some other n00b, then great. If I made you laugh at my fail, then that’s even better.
Thanks, everybody, for your help!
I like Markdown; it works very well. But it doesn’t have the ability to emphasize text with strikethroughs, which are a mainstay of modern conversation in blogs these days.
So I modified it. Watch this:
Here is a strikethrough.
See how easy that was? All I did was type a dash before and after the word “strikethrough,” and Markdown now makes that into a strikethrough.
To do this, all I did was modify the Markdown.pl code a bit. Here’s how you can do it, too.
Find Markdown.pl. For MoveableType users, it’s in mt/plugins/Markdown. Edit it with your favorite editor and go to line 1040. Make that block of code look like this by adding the last two or three lines (you can credit me if you want):
sub _DoItalicsAndBold {
my $text = shift;
# <strong> must go first:
$text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
{<strong>$2</strong>}gsx;
$text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 }
{<em>$2</em>}gsx;
# These lines added by Bill Eccles, 2008-07-04
$text =~ s{ (\s) (-) (?=\S) (.+?) (?<=\S) (-) }
{$1<strike>$3</strike>}gsx;
return $text;
}
Be forewarned that I’ve only tested it in very simple use (you’ve seen all the testing I’ve done, above) and that it might do unexpected things. But so far, so excellent good!
UPDATE: I wonder if this will make problem for people who-make-use of words-with hyphens-randomly scattered throughout their text… YES! AGH!
I’ll have to go work on that for a bit… back in a while.
UPDATE: OK, that problem is fixed (as you can see above). It does fail, however, with striking through hyphenated words, (Here’s an example-.) but that’s OK in my book. Just leave out the hyphen—it’d be lost in the strikethrough anyway.
Recent Comments
Joel Bernstein on More on Why I Think "two ex" Isn't As Likely As "any ex": The issue
Public Farley on I Side With Engadget: Cold Water on the iPad 2 Retina Display Hype | Daring Fireball: Nice post!
Bill Eccles on How Not to be Prepared for 24" of Snow (And Some Advice): I feel you
Janine F. on How Not to be Prepared for 24" of Snow (And Some Advice): Yeah, I sh
Greg on Note to Gruber: Democratic Congresswoman Gabrielle Giffords Shot in the Head at Public Event in Arizona | DaringFireball.net: This is ex
Bill Eccles on Designed to Fail: Apple Time Machine and MacOS X Server: Funny thin
Quentin Smith on Designed to Fail: Apple Time Machine and MacOS X Server: I just had
Anton E. on Building a Mac OS X Server 10.6: Step 1, Adventures With PHP [Updated 1/24/10 and 1/30/10]: Hi, I ve b
Arthur Nisnevich on Building a Mac OS X Server 10.6: Step 1, Adventures With PHP [Updated 1/24/10 and 1/30/10]: Thank you!