Jump to content
  • entries
    11
  • comments
    59
  • views
    14,283

Wireless weather station managed by the TI 99/4A - Final


Vorticon

5,481 views

So it's time for a new hardware interfacing project for the TI 99/4A, namely a wireless weather station managed by that venerable computer. I think it will be a fun project but likely with a steep learning curve. The goal is to have a functional prototype in time to demo at the 2017 Chicago Fair.

So where does one start? The most obvious would be finding a cost efficient and relatively easy way to achieve wireless communication between the various sensors and the computer. I believe the Xbee series 1 modules fit the bill quite nicely because they have a range of about 90 meters (300 feet) which is more than enough for the average user and they come pre-configured for serial communication. I chose the version that has a connector for an external antenna in order to maximize the transmission range.

blogentry-25753-0-47270900-1483117527_thumb.jpg

These modules have integrated ADC as well as digital inputs and transmit data serially which can be captured by a sister Xbee module connected to the computer. Unfortunately, they would not be enough by themselves because there are few sensors that can interface directly with them without some sort of processing first, and so a microcontroller is going to be needed for support and data packaging within the sensor array. With so much community support and experience available for the Arduino, I decided to go with the Arduino Uno using the Sparkfun Redboard.

blogentry-25753-0-86728700-1483118199_thumb.jpg

It comes with installed headers and connectors, making it easier to access and interface.

Now for the sensors:

While I could design and build my own mechanical components for wind speed, direction and rain level, it would be a pretty laborious process. So I opted to go with a ready made solution that is super easy to interface, again from Sparkfun:

blogentry-25753-0-69736300-1483118501_thumb.jpg

This is a purely mechanical device with no embedded electronics, and the data interpretation is via checking the state of various switches. What is even better is that it has a direct interface with the Sparkfun weather shield for the Arduino Uno which also includes pressure, humidity and luminosity sensors.

blogentry-25753-0-81793900-1483118673_thumb.jpg

All we need now is a temperature sensor, and the one below is inexpensive and pretty easy to use. It is analog, so it will be earmarked for one of the Xbee ADC inputs.

blogentry-25753-0-23885100-1483118796_thumb.jpg

This should round up the sensors for our weather station. The last thing we need is the Xbee serial explorer board to connect a receiver Xbee module to the TI 99/4A via the serial port.

blogentry-25753-0-72124300-1483118982_thumb.jpg

So far, this is going to cost around $215, and will likely need another $25 for various connectors and support items. So no, it's not exactly cheap, but the fun factors of putting all this together will be priceless!

Now what? Well, first of all I'm going to have to get familiar with the Arduino since I have had zero previous exposure to it. I have always used the Raspberry Pi for my other projects, but it would not have been suitable for this one. That is going to take a little time. But even before that, I'm going to have to dig deep into the Xbee documentation and come up with some communication tests, likely using only the temperature sensor for starters since it can connect directly to it, and see if I can transmit data wirelessly to the TI.

Update 1/28/17
First things first: Since the Xbee modules communicate via serial protocol, I needed to understand how that worked on the TI. It's definitively more involved than parallel communication, with a multitude of registers and CRU bits needed to be accessed in the 9902 UART chip in the RS232 card. Here Thierry Nouspikel's encyclopedic site ( http://www.unige.ch/medecine/nouspikel/ti99/titechpages.htm ) came in to the rescue with clear explanations and code examples.
Obviously this is all done in assembly which is fine. However, I recently found out the Rich Extended Basic (RXB) by Richard Gilbertson actually has a command called CALL IO which allows direct access to the CRU without resorting to assembly language! Below is an explanation by Lee Stewart about how it works:


CALL IO(type#,bits,cru-base/2,var,var)

If you set a variable named CRU to half the PIO CRU-base-address of >1300/2 = >0980 (2432), you can address CRU bits by adding to CRU in the call.

The following code will set, test and reset (clear) the indicated bits:

100 CRU = 2432
110 CALL IO(3,1,CRU,1) ' SBO 0 ;set bit 0
120 CALL IO(3,1,CRU+7,1) ' SBO 7 ;set bit 7
130 CALL IO(2,1,CRU+2,X) ' TB 2 ;test bit 2 and pass value to X
...
300 CALL IO(3,1,CRU,0) ' SBZ 0 ;reset bit 0


What this means is that at a minimum I could test my setup directly from within RXB without the need for coding in assembly with its associated cumbersome and time consuming development cycle of Edit/Compile/Run. And if it works out well, then there is a very good possibility that I might be able to code the entire project in RXB and bypass assembly altogether.

Update 1/31/17

At this stage I have configured 2 XBee modules for 9600bps 8N1 communication protocol, and attached one to an Arduino Uno via a shield, and the other to the serial port of the TI using an XBee serial adapter. The way XBee works is that if one of the modules receives data on its Data In line, it will serialize it and transmit it wirelessly to the Data Out line of all the other XBee modules in the same network. I have therefore connected the XBee DOUT line to the Arduino's Rx (receive) line, and the XBee DIN line to the Tx (transmit) line. There is a very small test program running on the Arduino which basically watches for data to show up on the Rx line, reads it, and if it is an 'a', it turns an LED on, and if it is a 'b', it turns that LED off.

On the TI side, I am running TELCO with the same communication parameters as the XBees, using a null modem serial cable. When I type 'a' in TELCO's terminal, the LED on Arduino lights up, and it turns off when I type in 'b'. Simple, but a great proof of concept nonetheless. Here's a quick video of the demo:

https://youtu.be/-9D0889Mks0

Update 2/27/17

On this next step, I switched over to using RXB on the TI side using the CALL IO functionality, and it worked beautifully, thus obviating the need to use assembly for this project. It took some fiddling and lots of help from the AtariAge TI community, particularly Stuart, to properly configure the TMS9901 chip in the RS232 card to work with the Xbee given that we are only using the Tx and Rx lines with no flow control whatsoever, but we eventually figured it out. The only downside of this kind of set up is that the Arduino is much faster than RXB, and so delays had to be introduced in order to get a reliable transmission, in the order of 500 msecs per byte transmitted.

From there, I moved on to actually connecting a temperature sensor to the remote arduino's analog inputs and tried to read the temperature data on the TI. The raw analog input from the sensor was converted by the Arduino to a float temperature value, which in turn was converted to a string and sent byte by byte to the TI. On the other end, the TI reassembled the string and converted the final product back to a float value. Below is the video demonstrating that experiment:

https://youtu.be/c9tPZM-42uY

Update 3/13/17

So I've decided to add a real time clock to the project with the idea of logging the various weather parameters for future analysis and comparison. To that effect, I acquired the Sparkfun RTC module based on the DS1307 chip and communicates to the microcontroller via the I2C (inter-inter chip) protocol. Sparkfun provides a specific library for the Arduino that makes access to the various date/time parameters trivial.

blogentry-25753-0-77205400-1489456146_thumb.jpg

Here's a demo of the process. Sorry about the quality of the video but I only had a low end camera on hand...

https://youtu.be/TtzBLftsX7E

Update 3/23/17

I am now in the process of testing the Sparkfun weather shield which has on board a pressure, humidity and light sensors. I'm still mulling over the use of the light sensor which would be useless if the whole setup was enclosed in a sealed box. On the other hand, if I add a clear window to the enclosure, I should be able to determine sunset and sunrise times which would be nice to have and log. In the mean time, the humidity and pressure sensors seem to work perfectly.
One issue that arose from using the weather shield is the fact that it utilizes all 6 analog inputs on the Arduino, and so interferes with the analog temperature sensor I am currently using. The solution is to replace that sensor with a one wire digital sensor which connects to a single digital input. I have opted to use the DS18B20 shown below. It is much more involved to set up on the Arduino side though, so we'll see how that goes. On the plus side, the sensor is weather proofed, so I will be able to have it hanging outside the enclosure without issues.

blogentry-25753-0-11117000-1490206104_thumb.jpg

https://youtu.be/NmMLNPq2_AA


Update 4/26/17

After a rather lengthy hiatus secondary to being super busy at work as well as some technical issues, I have finally managed to set up the last remaining sensors. I have replaced the analog temperature sensor with a digital one, and connected the wind speed, wind direction and rain gauge. This required getting familiar with some advanced Arduino topics, primarily the use of internal and external interrupts needed for the rain and wind sensors. I also made some boneheaded programming errors such as setting a float variable to integer instead of float and wondering why my values were always coming back as zero or forgetting to solder some of the pins to the board... Eventually everything worked out fine. I did end up connecting the two center pins of the rain RJ11 connector to ground and digital pin 9 on the weather shield so I could use internal (pin change) interrupts, but this was probably not necessary once I discovered that I had not soldered pin 2 of the shield which is used by the external interrupts. Live and learn :)

Here's the final Arduino sketch. I borrowed heavily from code examples on the net, and frankly Arduino programming is akin to building with Lego's where most sensors and interfaces come with set libraries that handle the low level programming and make it relatively easy to connect to these extensions. I have introduced a lot of delays in the program because there is no flow control between the TI and the Arduino and so I needed to allow the TI to process the data received without losses, particularly since I am using RXB and not assembly on the TI side. This translated into roughly an full update of all the sensors about once a minute, which is more than adequate for our purposes here.
 

 

#include <SparkFunDS1307RTC.h> // Real time clock#include "SparkFunMPL3115A2.h" // Pressure sensor #include "SparkFun_Si7021_Breakout_Library.h" // Humidity sensor#include <Wire.h> // I2C protocol#include <OneWire.h> // One wire protocol for temperature sensor#include <PinChangeInt.h> // Pin change interrupt libraryMPL3115A2 myPressure; // Create an instance of the pressure sensorWeather myHumidity; // Create an instance of the humidity sensor// Pin definitionsint DS18S20_Pin = 10; // DS18S20 Signal pin on digital 10const byte WSPEED = 3;const byte RAIN = 9;const byte WDIR = A0;//Temperature chip i/oOneWire ds(DS18S20_Pin); // on digital pin 10// Global variableslong lastWindCheck = 0;volatile long lastWindIRQ = 0;volatile byte windClicks = 0;volatile unsigned long raintime, raininterval;volatile unsigned long rainlast = 0;volatile float rainfall = 0;int windir = 0; // 0-360 instantaneous wind direction// Interrupt routinesvoid rainIRQ()// Count rain gauge bucket tips as they occur// Activated by the magnet and reed switch in the rain gauge, attached to input D9{    raintime = millis(); // grab the current time    raininterval = raintime - rainlast; // calculate interval between this and last event    if (raininterval > 10) // ignore switch-bounce glitches less than 10ms after initial edge    {      rainfall += 0.011;      rainlast = raintime; // set up for next event    }}void wspeedIRQ()// Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3{    if (millis() - lastWindIRQ > 10) // Ignore switch-bounce glitches less than 10ms    {      lastWindIRQ = millis(); // Grab the current time      windClicks++; // There is 1.492MPH for each click per second    }}void setup() {  Serial.begin(9600);  rtc.begin();  //Configure the pressure sensor  myPressure.begin(); // Get sensor online  myPressure.setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa  myPressure.setOversampleRate(7); // Set Oversample to the recommended 128  myPressure.enableEventFlags(); // Enable all three pressure and temp event flags  //Configure the humidity sensor  myHumidity.begin();  //Input from the anemometer sensor  pinMode(WSPEED, INPUT_PULLUP);  //Input from the rain gauge sensor  pinMode(RAIN, INPUT_PULLUP);  //Attach external interrupt pins to IRQ functions  PCintPort::attachInterrupt(RAIN, rainIRQ, FALLING);  attachInterrupt(1, wspeedIRQ, FALLING);  //Turn on interrupts  interrupts();}void loop() {  while(Serial.available()){    char getData = Serial.read();    switch (getData) {      case 'T': { // Request temperature readout in Celcius        float degreesC;        degreesC = getTemp();        String tempC = String(degreesC);        sendString(tempC);      }       break;            case 'H': { // Request humidity readout (%)        float humidity = myHumidity.getRH();        if (humidity == 998) //Humidty sensor failed to respond        {          //Try re-initializing the I2C comm and the sensors          myPressure.begin();           myPressure.setModeBarometer();          myPressure.setOversampleRate(7);          myPressure.enableEventFlags();          myHumidity.begin();        }        else Serial.write(int(humidity));      }      break;            case 'P': { // Request pressure readout in Pa        float pressure = myPressure.readPressure();        String pressurePa = String(pressure);        sendString(pressurePa);      }      break;            case 'C': { // Request clock readout        rtc.update();         int m = rtc.minute();        int h = rtc.hour();        int dy = rtc.day(); // day of week. 1=Sunday        int da = rtc.date();        int mo = rtc.month();        int yr = rtc.year();        delay(500);        Serial.write(h);        delay(500);        Serial.write(m);        delay(500);        Serial.write(dy);        delay(500);        Serial.write(da);        delay(500);        Serial.write(mo);        delay(500);        Serial.write(yr);      }      break;      case 'W': { // Request wind speed        float deltaTime = millis() - lastWindCheck;        deltaTime /= 1000.0; // Convert to seconds        float windSpeed = (float)windClicks / deltaTime;        lastWindCheck = millis();        windSpeed *= 1.492; // Convert to MPH        String windSpeedMPH = String(windSpeed);        sendString(windSpeedMPH);        windClicks = 0;      }      break;            case 'D': { // Request wind direction        int windDir = get_wind_direction();        String winDirDeg = String(windDir);        sendString(winDirDeg);      }      break;      case 'R': { // Request rain fall in inches since last check        String rainFallInches = String(rainfall);        rainfall = 0;        sendString(rainFallInches);      }      break;            break;    }  }}void sendString(String readVal) // Sends a value as a series of characters{  int i,valLength;    valLength = readVal.length();  delay(500);  Serial.write(valLength);  delay(500);  for (i = 0; i < valLength; i++){    char digit = readVal.charAt(i);    delay(500);    Serial.write(byte(digit));    delay(500);  }}float getTemp(){  //returns the temperature from one DS18S20 in DEG Celsius  byte data[12];  byte addr[8];  if ( !ds.search(addr)) {      //no more sensors on chain, reset search      ds.reset_search();      return -1000;  }  if ( OneWire::crc8( addr, 7) != addr[7]) {      return -1000;  }  if ( addr[0] != 0x10 && addr[0] != 0x28) {      return -1000;  }  ds.reset();  ds.select(addr);  ds.write(0x44,1); // start conversion, with parasite power on at the end    delay(750); // Wait for temperature conversion to complete  byte present = ds.reset();  ds.select(addr);      ds.write(0xBE); // Read Scratchpad    for (int i = 0; i < 9; i++) { // we need 9 bytes    data[i] = ds.read();  }    ds.reset_search();    byte MSB = data[1];  byte LSB = data[0];  float tempRead = ((MSB <<  | LSB); //using two's compliment  float TemperatureSum = tempRead / 16;    return TemperatureSum;}int get_wind_direction() {  //Read the wind direction sensor, return heading in degrees  unsigned int adc;  adc = analogRead(WDIR); // get the current reading from the sensor  // The following table is ADC readings for the wind direction sensor output, sorted from low to high.  // Each threshold is the midpoint between adjacent headings. The output is degrees for that ADC reading.  if (adc < 380) return (113);  if (adc < 393) return (68);  if (adc < 414) return (90);  if (adc < 456) return (158);  if (adc < 508) return (135);  if (adc < 551) return (203);  if (adc < 615) return (180);  if (adc < 680) return (23);  if (adc < 746) return (45);  if (adc < 801) return (248);  if (adc < 833) return (225);  if (adc < 878) return (338);  if (adc < 913) return (0);  if (adc < 940) return (293);  if (adc < 967) return (315);  if (adc < 990) return (270);  return (-1); // error, disconnected?}
 

 



And here's the RXB program. Please bear in mind that this is only a test program and definitely not the final product.
 

 

100 CALL CLEAR110 CRU=2464!CRU ADDRESS OF TMS9902 DIVIDED BY 2120 CALL IO(3,1,2432,1)!ACTIVATE RS232 CARD130 CALL IO(3,1,CRU+31,1)!RESET TMS9902140 CALL PEEK(12,CLKFREQ)!CHECK CONSOLE CLOCK FREQUENCY150 IF CLKFREQ<>40 THEN 200160 CALL IO(3,8,CRU,195)!LOAD CONTROL REGISTER WITH 8N1 @ 2.5MHZ170 CALL IO(3,1,CRU+13,0)!DESELECT THE INTERVAL REGISTER180 CALL IO(3,12,CRU,0,43)!LOAD RECEPTION AND EMISSION RATE REFISTERS WITH 9600 BPS190 GOTO 140200 CALL IO(3,8,CRU,203)!LOAD CONTROL REGISTER WITH 8N1 @ 3MHZ210 CALL IO(3,1,CRU+13,0)!DESELECT THE INTERVAL REGISTER220 CALL IO(3,12,CRU,0,39)!LOAD RECEPTION AND EMISSION RATE REGISTERS WITH 9600 BPS230 FOR I=1 TO 7 :: READ D$(I) :: NEXT I240 DATA SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY250 CALL IO(3,1,CRU+16,1)!ACTIVATE RTS255 REM FETCH TIME AND DATE260 PRINT "CLOCK REQUEST SENT..." :: CALL IO(3,8,CRU,67)270 GOSUB 470280 IF FLAG=0 THEN 260290 CALL IO(2,8,CRU,HOUR)! FETCH THE HOUR300 GOSUB 470 :: IF FLAG=0 THEN 260310 CALL IO(2,8,CRU,MINUTE)! FETCH THE MINUTE320 GOSUB 470 :: IF FLAG=0 THEN 260330 CALL IO(2,8,CRU,DOW)! FETCH DAY OF WEEK340 GOSUB 470 :: IF FLAG=0 THEN 260350 CALL IO(2,8,CRU,DAY)! FETCH DAY360 GOSUB 470 :: IF FLAG=0 THEN 260370 CALL IO(2,8,CRU,MONTH)! FETCH MONTH380 GOSUB 470 :: IF FLAG=0 THEN 260390 CALL IO(2,8,CRU,YEAR)400 PRINT :: PRINT D$(DOW);" ";MONTH;"/";DAY;"/";YEAR;" " :: PRINT HOUR;":";MINUTE405 REM FETCH TEMPERATURE410 CALL IO(3,1,CRU+18,0) :: PRINT :: PRINT "TEMPERATURE REQUEST SENT..." :: CALL IO(3,8,CRU,84)420 GOSUB 470 :: IF FLAG=0 THEN 410430 GOSUB 520440 IF FLAG=0 THEN 410450 PRINT :: PRINT N;" CELCIUS" :: PRINT N*9/5+32;" FARENHEIT"460 PRINT :: GOTO 640465 REM *** SUBROUTINES ***470 REM  CHECK FOR INCOMING BYTE ROUTINE480 COUNTER=0490 CALL IO(2,1,CRU+21,BYTEIN)500 IF BYTEIN=0 THEN COUNTER=COUNTER+1 ELSE FLAG=1 :: CALL IO(3,1,CRU+18,0) :: RETURN510 IF COUNTER>50 THEN FLAG=0 :: CALL IO(3,1,CRU+18,0) :: RETURN ELSE 490520 REM  RECEIVE STRING ROUTINE530 CALL IO(2,8,CRU,LENGTH)! FETCH LENGTH OF STRING540 CALL IO(3,1,CRU+18,0)! RESET THE RECEIVE BUFFER550 FOR I=1 TO LENGTH560 GOSUB 470 :: IF FLAG=0 THEN RETURN570 CALL IO(2,8,CRU,D(I))! RECEIVE DATA580 CALL IO(3,1,CRU+18,0)!RESET THE RECEIVE BUFFER590 NEXT I600 N$=""610 FOR I=1 TO LENGTH :: N$=N$&CHR$(D(I)) :: NEXT I620 N=VAL(N$)630 RETURN634 REM *** END SUBROUTINES ***635 REM FETCH HUMIDITY640 CALL IO(3,1,CRU+18,0) :: PRINT :: PRINT "HUMIDITY REQUEST SENT..." :: CALL IO(3,8,CRU,72)650 GOSUB 470 :: IF FLAG=0 THEN 640660 CALL IO(2,8,CRU,HUMIDITY)! RECEIVE HUMIDITY VALUE670 PRINT :: PRINT "HUMIDITY = ";HUMIDITY;"%" :: PRINT675 REM FETCH PRESSURE680 CALL IO(3,1,CRU+18,0) :: PRINT :: PRINT "PRESSURE REQUEST SENT..." :: CALL IO(3,8,CRU,80)690 GOSUB 470 :: IF FLAG=0 THEN 680700 GOSUB 520 :: IF FLAG=0 THEN 680710 PRINT :: PRINT "PRESSURE = ";N/1000;"KPa"::PRINT730 REM FETCH WIND SPEED740 CALL IO(3,1,CRU+18,0)::PRINT::PRINT "WIND SPEED REQUEST SENT..."::CALL IO(3,8,CRU,87)750 GOSUB 470::IF FLAG=0 THEN 740760 GOSUB 520::IF FLAG=0 THEN 740770 PRINT::PRINT "WIND SPEED = ";N;"MPH"::PRINT780 REM FETCH WIND DIRECTION790 CALL IO(3,1,CRU+18,0)::PRINT::PRINT "WIND DIRECTION REQUEST SENT..."::CALL IO(3,8,CRU,68)800 GOSUB 470::IF FLAG=0 THEN 790810 GOSUB 520::IF FLAG=0 THEN 790820 PRINT::PRINT "WIND DIRECTION = ";N;"DEGREES"::PRINT830 REM FETCH RAIN FALL840 CALL IO(3,1,CRU+18,0)::PRINT::PRINT "RAIN FALL REQUEST SENT..."::CALL IO(3,8,CRU,82)850 GOSUB 470::IF FLAG=0 THEN 840860 GOSUB 520::IF FLAG=0 THEN 840870 PRINT::PRINT "RAIN FALL = ";N;"INCHES"::PRINT::GOTO 260
 

 



And here's the demo video:

https://youtu.be/51o0DZ_8CwE

Next is tidying up the electronics and designing an proper enclosure which I will likely 3D print. Then I will need to create the final RXB interface program and look into data logging options.

Update 5/7/17

One thing we did not discuss to date is how to power up the wireless station. I suppose I could run an electrical wire to it but that would be cumbersome at best and against the spirit of "wireless"! So the obvious solution was to use solar power. To that end, got the Sparkfun Sunny Buddy solar charger module, a 9W solar panel and a 2Ah 3.7V LIPO rechargeable battery.

blogentry-25753-0-31080400-1494179702_thumb.jpg

blogentry-25753-0-50265800-1494179709_thumb.jpg

blogentry-25753-0-03958000-1494179788_thumb.jpg

The Sunny Buddy is a smart module that maximizes the solar powered charging process and delivers needed power to whatever project you need. It does however come setup for 450mA output charge by default, but there is a provision to add a resistor in parallel to the sense resistor to increase that current to whatever is required. The 2Ah battery should hopefully provide enough juice for uninterrupted operation at night, and the 9W solar panel is large enough for quick battery charging providing 1500mA at peak (1.4hrs charge time).

UPDATE: So I feel pretty dumb at this moment... While the Sunny Buddy coupled with the solar panel charged the battery just fine, I just realized that the Arduino Red Board I have requires at least 7V for operation... Doh... Soooo, I ordered another LIPO battery with similar specs along with a second Sunny Buddy which I will connect in parallel to the solar panel and the batteries output will be in series connecting to the Arduino. The main implication here, aside for the additional cost involved :mad:, is that now it will take 2.7hrs in full sun to charge the batteries compared to 1.4hrs previously. Still pretty decent nonetheless.
I also finalized the layout of the electronics, with easy access to the various components in case repairs are needed. Next is the case design, which should be pretty straightforward...

https://youtu.be/V9xVJqf-tIY

Final Update 6/8/17

So everything is now put together and working as it should. Yay! I'm still struggling with the use of solar power to power up the project for the following reasons:

  • My house is surrounded by tall trees, and so any particular spot in my back or front yard will only get a maximum of 6hrs of direct sunlight
  • The batteries can only power up the project for at most 10hrs or so
  • It takes approximately 3.5-4hrs of direct sunlight to fully charge the batteries


Therefore, starting out with fully charged batteries when no direct sun is available anymore, say 4pm, the system remains powered up till 2am the following day, although I have found that on clear days there is still a trickle charge from the solar panel to stretch operational time by an extra 2hrs. Even then, if the batteries die out at 4am, the sun doesn't peak over the trees till 10am, so there is a 6hr gap right there. Furthermore, since it takes 3.5-4hrs to charge the batteries, the system doesn't actually get back online till 2pm, giving me a total downtime of 10hrs! And it's worse if it's cloudy... Of course I could use larger capacity batteries, but then they would not fit in the enclosure and would need proportionately larger solar panels to recharge...
But this issue is really only particular to my current home location. In MN, sunrise is around 7am and sunset around 9pm in summer, and so if I had that full range of solar radiation accessible, the downtime would be much shorter...
I have toyed with the idea of reducing polling frequency at night to conserve power, but then I would lose a lot of data that way, particularly if it's raining. Preliminary tests did not show a substantial saving in battery power anyway.
Regardless, the project still works as expected, although in my case I might forgo solar power altogether and just use electrical power from the mains with a wireless connection to the TI in the basement. I have found that the effective maximum range was in the order of 70 feet, going through several walls, but your range might vary depending on where the TI is located.

Here's a video of the final setup and testing:

https://youtu.be/QPheRrUmIYE

And here's the source code for the control program on the TI using RXB.

 

100 REM  TI WIRELESS WEATHER STATION110 REM  CONTROL PROGRAM120 REM  BY WALID MAALOULI130 REM  MAY 2017140 OPEN #1:"DSK2.LOG_DAT",INTERNAL,INPUT150 INPUT #1:MAXTEMP,MXTMONTH,MXTDAY,MXTYEAR,MINTEMP,MITMONTH,MITDAY,MITYEAR,TRAIN,RMONTH,RDAY,RYEAR160 CLOSE #1170 CALL CLEAR :: OPTION BASE 1180 CALL HPUT(1,1,"RESET CUMULATIVE RAINFALL?") :: CALL BEEP190 CALL KEY("YN",0,K,S) :: IF K=89 THEN RFLAG=1200 CALL CLEAR :: DIM SLEEPTIME(12),WAKETIME(12)210 GOSUB 1760220 FOR I=1 TO 7 :: READ D$(I) :: NEXT I230 DATA sunday,monday,tuesday,wednesday,thursday,friday,saturday240 CALL CHAR(31,"FFFFFFFFFFFFFFFF",123,"8080808080808080",124,"0101010101010101")250 FOR I=1 TO 14 :: PROGBAR$=PROGBAR$&CHR$(31) :: NEXT I260 FOR I=1 TO 12 :: READ SLEEPTIME(I),WAKETIME(I) :: NEXT I270 DATA 16,8,16,8,16,8,17,7,17,7,18,7,18,7,18,7,17,7,17,7,17,7,16,8280 DTMAX=-99 :: DTMIN=120 :: MAXSPEED=-1290 REM  DRAW THE MAIN SCREEN300 GOSUB 1240310 ON ERROR 1850320 CALL HPUT(24,17,"              ")330 REM   FETCH TIME AND DATE340 CALL IO(3,8,CRU,67)350 GOSUB 1470360 IF FLAG=0 THEN 340370 CALL IO(2,8,CRU,HOUR)! FETCH THE HOUR380 GOSUB 1470 :: IF FLAG=0 THEN 340390 CALL IO(2,8,CRU,MINUTE)! FETCH THE MINUTE400 GOSUB 1470 :: IF FLAG=0 THEN 340410 CALL IO(2,8,CRU,DOW)! FETCH DAY OF WEEK420 GOSUB 1470 :: IF FLAG=0 THEN 340430 CALL IO(2,8,CRU,DAY)! FETCH DAY440 GOSUB 1470 :: IF FLAG=0 THEN 340450 CALL IO(2,8,CRU,MONTH)! FETCH MONTH460 GOSUB 1470 :: IF FLAG=0 THEN 340470 CALL IO(2,8,CRU,YEAR)! FETCH YEAR480 PROGSTAT=1 :: GOSUB 1430490 IF SLEEPFLAG=0 AND(HOUR>=SLEEPTIME(MONTH)OR HOUR<WAKETIME(MONTH))THEN SLEEPINT=1000 :: GOTO 520500 IF SLEEPFLAG=0 THEN SLEEPINT=100 :: GOTO 520510 IF HOUR<SLEETIME(MONTH)AND HOUR>=WAKETIME(MONTH)THEN SLEEPFLAG=0 :: SLEEPINT=100520 REM   FETCH TEMPERATURE530 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,84)540 GOSUB 1470 :: IF FLAG=0 THEN 530550 GOSUB 1520560 IF FLAG=0 THEN 530 ELSE TEMP=INT(N*9/5+32)570 PROGSTAT=2 :: GOSUB 1430580 REM   FETCH HUMIDITY590 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,72)600 GOSUB 1470 :: IF FLAG=0 THEN 590610 CALL IO(2,8,CRU,HUMIDITY)! RECEIVE HUMIDITY VALUE620 PROGSTAT=3 :: GOSUB 1430630 REM   FETCH PRESSURE640 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,80)650 GOSUB 1470 :: IF FLAG=0 THEN 640660 GOSUB 1520 :: IF FLAG=0 THEN 640 ELSE PRESSURE=INT(N/1000)670 PROGSTAT=4 :: GOSUB 1430680 REM   FETCH WIND SPEED690 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,87)700 GOSUB 1470 :: IF FLAG=0 THEN 690710 GOSUB 1520 :: IF FLAG=0 THEN 690 ELSE WSPEED=N720 PROGSTAT=5 :: GOSUB 1430730 REM   FETCH WIND DIRECTION740 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,68)750 GOSUB 1470 :: IF FLAG=0 THEN 740760 GOSUB 1520 :: IF FLAG=0 THEN 740 ELSE WDIR=N770 PROGSTAT=6 :: GOSUB 1430780 REM   FETCH RAIN FALL790 CALL IO(3,1,CRU+18,0) :: CALL IO(3,8,CRU,82)800 GOSUB 1470 :: IF FLAG=0 THEN 790810 GOSUB 1520 :: IF FLAG=0 THEN 790 ELSE DRAIN=DRAIN+N820 PROGSTAT=7 :: GOSUB 1430830 REM  DISPLAY DATA840 FOR I=1 TO 19 :: CALL HPUT(I,25,"    ") :: NEXT I850 CALL HPUT(1,7,"                       ")860 IF MINUTE<10 THEN MINUTE$="0"&STR$(MINUTE)ELSE MINUTE$=STR$(MINUTE)870 IF HOUR<10 THEN HOUR$="0"&STR$(HOUR)ELSE HOUR$=STR$(HOUR)880 TIME$=HOUR$&":"&MINUTE$890 IF MONTH<10 THEN MONTH$="0"&STR$(MONTH)ELSE MONTH$=STR$(MONTH)900 IF DAY<10 THEN DAY$="0"&STR$(DAY)ELSE DAY$=STR$(DAY)910 DATE$=MONTH$&"\"&DAY$&"\"&STR$(YEAR)920 CALL HPUT(1,7,D$(DOW)&" "&DATE$,2,7,TIME$)930 CALL HPUT(4,25,TEMP,4,29,"f")940 IF HOUR=0 THEN DTMAX=-99 :: DTMIN=120950 IF DTMAX<TEMP THEN DTMAX=TEMP :: DTMAXHR$=HOUR$ :: DTMAXMN$=MINUTE$960 IF DTMIN>TEMP THEN DTMIN=TEMP :: DTMINHR$=HOUR$ :: DTMINMN$=MINUTE$970 CALL HPUT(5,7,DTMAXHR$,5,9,":",5,10,DTMAXMN$,5,25,DTMAX,5,29,"f")980 CALL HPUT(6,7,DTMINHR$,6,9,":",6,10,DTMINMN$,6,25,DTMIN,6,29,"f")990 IF MAXTEMP<TEMP THEN MAXTEMP=TEMP :: MXTMONTH=MONTH :: MXTDAY=DAY :: MXTYEAR=YEAR :: CALL IO(3,1,2432,0) :: GOSUB 1680 :: GOSUB 17501000 CALL HPUT(7,12,"        ",8,12,"        ")1010 IF MINTEMP>TEMP THEN MINTEMP=TEMP :: MITMONTH=MONTH :: MITDAY=DAY :: MITYEAR=YEAR :: CALL IO(3,1,2432,0) :: GOSUB 1680 :: GOSUB 17501020 MXTDATE$=STR$(MXTMONTH)&"\"&STR$(MXTDAY)&"\"&STR$(MXTYEAR)1030 MITDATE$=STR$(MITMONTH)&"\"&STR$(MITDAY)&"\"&STR$(MITYEAR)1040 CALL HPUT(7,12,MXTDATE$,7,25,MAXTEMP,7,29,"f")1050 CALL HPUT(8,12,MITDATE$,8,25,MINTEMP,8,29,"f")1060 CALL HPUT(10,25,PRESSURE,10,29,"kpa")1070 CALL HPUT(12,25,HUMIDITY,12,29,"%")1080 CALL HPUT(14,25,WSPEED,14,29,"mph")1090 CALL HPUT(15,25,WDIR,15,29,"deg")1100 IF HOUR=0 THEN MAXSPEED=-11110 IF MAXSPEED<WSPEED THEN MAXSPEED=WSPEED :: MSHR$=HOUR$ :: MSMN$=MINUTE$1120 CALL HPUT(16,7,MSHR$,16,9,":",16,10,MSMN$,16,25,MAXSPEED,16,29,"mph")1130 IF HOUR=0 THEN DRAIN=N1140 CALL HPUT(18,25,DRAIN,18,30,"in")1150 TRAIN=TRAIN+N1160 IF RMONTH=0 OR RFLAG=1 OR RYEAR<YEAR THEN RMONTH=MONTH :: RDAY=DAY :: RYEAR=YEAR :: RFLAG=0 :: CALL IO(3,1,2432,0) :: GOSUB 1680 :: GOSUB 17501170 IF HOUR=0 THEN CALL IO(3,1,2432,0) :: GOSUB 1680 :: GOSUB 17501180 RDATE$=STR$(RMONTH)&"\"&STR$(RDAY)&"\"&STR$(RYEAR)1190 CALL HPUT(19,13,"        ")1200 CALL HPUT(19,13,RDATE$,19,25,TRAIN,19,30,"in")1210 CALL HPUT(24,17,"              ")1220 GOSUB 1640 :: GOTO 3301230 REM  ** SUBROUTINES **1240 REM  DRAW MAIN SCREEN1250 CALL SCREEN(2) :: CALL COLOR(ALL,4,2,3,16,2,4,16,2,9,16,2,10,16,2,11,16,2,12,16,2)1260 FOR I=65 TO 90 :: CALL DUPCHAR(I,I+32) :: NEXT I1270 CALL HPUT(1,1,"DATE:")1280 CALL HPUT(2,1,"TIME:")1290 CALL HPUT(4,1,"CURRENT TEMPERATURE   :")1300 CALL HPUT(5,1,"MAX @                 :")1310 CALL HPUT(6,1,"MIN @                 :")1320 CALL HPUT(7,1,"HIGHEST ON            :")1330 CALL HPUT(8,1,"LOWEST ON             :")1340 CALL HPUT(10,1,"BAROMETRIC PRESSURE   :")1350 CALL HPUT(12,1,"HUMIDITY              :")1360 CALL HPUT(14,1,"WIND SPEED            :")1370 CALL HPUT(15,1,"WIND DIRECTION        :")1380 CALL HPUT(16,1,"MAX @                 :")1390 CALL HPUT(18,1,"RAIN FALL TODAY       :")1400 CALL HPUT(19,1,"TOTAL SINCE           :")1410 CALL HPUT(24,1,"UPDATE PROGRESS",23,17,CHR$(123),23,30,CHR$(124))1420 RETURN1430 REM  PROGRESS BAR ROUTINE1440 PROG$=SEG$(PROGBAR$,1,PROGSTAT*2)1450 CALL HPUT(24,17,PROG$) :: CALL BEEP1460 RETURN1470 REM  CHECK FOR INCOMING BYTE ROUTINE1480 COUNTER=01490 CALL IO(2,1,CRU+21,BYTEIN)1500 IF BYTEIN=0 THEN COUNTER=COUNTER+1 ELSE FLAG=1 :: CALL IO(3,1,CRU+18,0) :: RETURN1510 IF COUNTER>50 THEN FLAG=0 :: CALL IO(3,1,CRU+18,0) :: RETURN ELSE 14901520 REM  RECEIVE STRING ROUTINE1530 CALL IO(2,8,CRU,LENGTH)! FETCH LENGTH OF STRING1540 CALL IO(3,1,CRU+18,0)! RESET THE RECEIVE BUFFER1550 FOR I=1 TO LENGTH1560 GOSUB 1470 :: IF FLAG=0 THEN RETURN1570 CALL IO(2,8,CRU,D(I))! RECEIVE DATA1580 CALL IO(3,1,CRU+18,0)!RESET THE RECEIVE BUFFER1590 NEXT I1600 N$=""1610 FOR I=1 TO LENGTH :: N$=N$&CHR$(D(I)) :: NEXT I1620 N=VAL(N$)1630 RETURN1640 REM  SET INTERVAL TIMER ROUTINE1650 IF SLEEPINT=1000 THEN SLEEPFLAG=1 :: CALL HPUT(23,1,"sleep mode")1660 FOR I=1 TO SLEEPINT :: NEXT I1670 CALL HPUT(23,1,"          ") :: RETURN1680 REM  UPDATE LOG DISK FILE1690 OPEN #1:"DSK2.LOG_DAT",INTERNAL,UPDATE1700 PRINT #1:MAXTEMP,MXTMONTH,MXTDAY,MXTYEAR1710 PRINT #1:MINTEMP,MITMONTH,MITDAY,MITYEAR1720 PRINT #1:TRAIN,RMONTH,RDAY,RYEAR1730 CLOSE #11740 RETURN1750 REM  SET UP RS232 ROUTINE1760 CRU=2464!CRU ADDRESS OF TMS9902 DIVIDED BY 21770 CALL IO(3,1,2432,1)! ACTIVATE RS232 CARD1780 CALL IO(3,1,CRU+31,1)!RESET TMS99021790 CALL IO(3,8,CRU,203)!LOAD CONTROL REGISTER WITH 8N1 @ 3MHZ1800 CALL IO(3,1,CRU+13,0)!DESELECT THE INTERVAL REGISTER1810 CALL IO(3,12,CRU,0,39)!LOAD RECEPTION AND EMISSION RATE REGISTERS WITH 9600 BPS1820 CALL IO(3,1,CRU+16,1)! ACTIVATE RTS1830 RETURN1840 REM  ERROR RETURN1850 CALL BEEP :: CALL HPUT(23,1,"ERROR DETECTED!") :: FOR I=1 TO 2000 :: NEXT I :: CALL CLEAR1860 RETURN 300
 

 



So this has been a really fun and hugely educational project for me. I hope you have enjoyed it as well :)
On to the next adventure!



 

WEATHER.dsk

  • Like 8
  • Thanks 1

16 Comments


Recommended Comments

You bet I'll stay tuned! This looks like a totally AWESOME project!

 

This would be great with an interface to JediMatt42's Plug-N-Play 32K unit. Maybe a 3D case printed up and the controlling software stuck in the FlashROM 99. (just thinking out loud).

  • Like 1
Link to comment

Very interesting project which may lead to other similar ventures from the knowledge you will bring to the table. Interfacing wirelessly is opening a lot of doors.

 

Good Luck Vorticon.

  • Like 1
Link to comment

Davvel,

Vorticon's become the resident "Interfacing Guru" within the TI community. If it can be controlled or monitored by the TI, Vorticon's your guy!

Link to comment

Thanks for the compliment Omega, but I think you credit me with far more ability than I actually have! I do love to tinker that's for sure, and I think that's the main ingredient here :)

  • Like 1
Link to comment

You're too modest Vorticon. I've been watching your interfacing projects with awe and amazement ever since you made that kid so happy getting his TI to launch rockets.

 

From back in the day I've always known that the cassette port, joystick port and audio and power out on the back were sorely overlooked and could be all be used together for some truly great stuff. Sadly, back then computers were evolving much faster pace, and then the TI was abandoned and people moved on, and so did my hopes. Who knew that 30+ years later the TI would still be around and that a guy like you would be around to finally exploit all the unrealized potential of a TI console. I'm a true fan.

 

Your starting point with only one temperature sensor is exciting in itself. Once you figure out how to share the data with the TI you'll be 3/4 of the way there. Who knows... if we are incredibly lucky by this time next year your blog might even have downloadable code and instructions for us to construct our own. (No pressure or anything like that though.) :)

Link to comment

New York State spent $30million to update its weather data collection and prediction capabilities. Just wanted to mention this before you order your connectors and support items.

 

It is all about the fun. : )

  • Like 1
Link to comment

 

It is all about the fun. : )

 

Yes it is indeed :) Besides, I'm looking forward to being able to measure my own local micro-climate rather than rely on macro data from the weather services.

As far as I am concerned, each project I embark on, regardless of how outrageously useless it is, remains a learning experience which I can build on in future projects. Here, I learned to program the Arduino, learned to set up the XBee modules, learned about the hidden capabilities of RXB, and leaned about serial communication on the TI. That's a heck of a lot of value right there, something I will most certainly use in the future.

  • Like 1
Link to comment

Yeah it's moving right along... I'm almost done with testing the entire array of sensors, and once that's completed I'll start working on the software interface and the data logging. I'll also start designing a 3D printed enclosure for the sensors and arduino.

Link to comment

Thanks :) I'm glad I was able to get everything working as intended. There were a few times when I had some doubts...

Link to comment

Awesome project! It's been fun watching your progress along the way.

 

A couple of questions...

 

1) Do all the shields combined have more computing power than the TI?

2) What was the final cost? ;)

3) Will you be taking orders? (just kidding).

Link to comment

I'm pretty sure the Arduino can outrun the TI in most situations :) There is definitely a lot of computing power going on here with all the shields. The cost was close to $500... The solar panel by itself was about $80! So I guess that precludes any commercial run he he... But then, the cost was spread out over several months as the need for new parts arose, so I guess it was not as painful as it sounds.

I suppose you can't put a price on geeky fun :)

Link to comment

Nicely done.

 

Was there any one shield that drained the battery or was it the Arduino? I thought that it might be the XBEE.

Link to comment

Nicely done.

 

Was there any one shield that drained the battery or was it the Arduino? I thought that it might be the XBEE.

 

Thanks!

I have not done any specific testing on power consumption, but I agree that it's likely the Xbee shield that takes the lion's share of that. RF transmission is usually a power hog...

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...