RoadTest: RoadTest the ADS7042 BoosterPack and MSP430FR4133
Author: ajoyraman
Creation date:
Evaluation Type: Independent Products
Did you receive all parts the manufacturer stated would be included in the package?: True
What other parts do you consider comparable to this product?: Considered low power micro-controlles from Microchip
What were the biggest problems encountered?: Integrating the ADC of ADS7042 along with the pFatFs library in LPM3.5 mode so that ADC and SD-Card write could function simultaneously in the active period and be inactive during sleep in LPM3.5. and achieving the lowest power consumption.
Detailed Review:
It was a privilege and pleasure to conduct this RoadTest. I decided to build a practical application in order to exhaustively check out the features of the combination of the MSP-EXP430FR4133 LaunchPad development kit and the ADS7042 Ultra-Low Power Data Acquisition BoosterPack.
The full development details are available at: Full Development Details
1. Project title: Anemometer and Wind-Vane Wind-Speed and Direction Logger
2. Project brief: A stand-alone unit operating from three AA batteries which is capable of measuring and logging wind-speed and wind-direction on a SD-card for a period of one year at intervals of 5 minutes or less with simultaneous display of interval-count, speed and direction on the LCD.
Using DIY hardware wind-speed is sensed by a cup-anemometer driving a small dc-motor acting as a dc-generator with output is in the range 0 to 300 mV. Wind-direction is sensed by a wind-vane which drives a one-wire potentiometer with a resistance range 0 to 24 kilo Ohm.
After signal-conditioning wind-speed is A/D converted by the ADS7042 and the wind-direction is A/D converted by one of the ADC channels of the MSP430FR4133. To achieve the lowest standby current the MSP430FR4133 is operated in LPM3.5 mode and the circuitry required for signal-conditioning, A/D conversion and writing to SD-card are switched ON only during the desired measurement interval.
An ‘Anemometer Booster Pack’ incorporating all additional circuitry and SD-card is specially designed and integrated below the MSP430FR4133 LaunchPad development kit while the ADS7042 BoosterPack with minor modifications is fixed above.
3. Observations:
3.1. The OutOfBox_MSP430FR4133 example software compiled and ran without difficulty on CCS Cloud in ‘c’.
3.2. Following the Ti instructions and imported the BOOST-ADS7042_Firmware.ino file into CCS Cloud. It compiled and ran without any difficulty using Energia.
3.3. Three different methods for programming the MSP430FR4133 worked successfully:
3.3.1. Using FET-Pro430-Lite-3v41
3.3.2. Using the command line Flasher MSPFlasher.exe
3.3.3. Using the Cloud Based Flasher 'UniFlash'
3.4. After working with CCS-Cloud, CCS-6 and Energia-18 application development was continued in Energia-18 primarily because of ease in incorporating the Petit FatFS SD Card Library
3.5. Three modifications were carried out on the ADS7042 booster pack:
3.5.1. First the 3.3V line to the power LED (Green) before R34 was cut in order to reduce the card current drain of the LED.
3.5.2. Second the 3.3V line from the 3.3V tag to the SMD fuse F1 was cut and an additional wire connected at the input end of F1. This permits switching power to the booster pack as required.
3.5.3. An additional wire was connected at JP1 pin 2 for feeding the signal to be A/D converted instead of using the SMA connector for input.
3.6. Software development was carried out in stages. There was no difficulty in checking the LCD, ADC of MSP430FR4133, ADC of ADS7042 and the SD-card functions individually. Inconstancy was noticed when the ADS7042 and the SD-card functions were used together in LPM 3.5 mode.
The Energia-18 compiler gave inconsistent results when the SPI function from the example program for ADS7042 and the pfatfs functions were used together. Apparently the SPI function defined in pfatfs affects the SPI code defined for the ADS7042.
This problem was overcome by calling the ADS7042 ADC function during the period when pfatfs was open. However the combination of the ADC result MSB and LSB had to be modified to get the correct value.
3.7. A switching scheme was devised whereby the supply 3.3V to the booster pack and the ground to the SD-card could be selected by software control. In LPM 3.5 mode the standby current measured was 0.01mA and the maximum current drawn was ~30mA during SD-card write.
As the Vref on ADS7042 booster pack has been chosen as 3.0V the supply required is 3.3V. This required three AA batteries and an external 3.3V regulator. The very low quiescent LDO Ti LM2936 was chosen for this.
With this LDO supplying 3.3V to the integrated set-up from an input of 4.5V the detailed current profile was measured to be 0.03mA when in sleep during LPM 3.5 and 38 for 0.5 sec during SD-card write, 22mA for .2 sec during SD-card close and another 1.5 mA for 1.7 sec during the active period. This totals to 25.95 mAsec for the active period of 2.4 sec.
Considering a write interval of 5 min the estimate of power drawn for 1 year would be: (25.95 mAsec (active) + 8.93 mAsec (sleep)) x 12 x 24 x 365 = 3666585.6 mAsec or 1018.496 mAH. Alkaline AA batteries with 1.2AH capacity should theoretically meet the one year requirement.
4. Results
4.1. A quick evaluation was carried out on the A/D converters of the ADS7042 and MSP430FR4133 the results are show below:
|
4.2 Current Profile in LPM 3.5 update every 16 seconds
|
---|
4.3. Typical log file output 1 minute update
Day 006 01:25 009 km/hr 061 deg
Day 006 01:26 009 km/hr 062 deg
Day 006 01:27 009 km/hr 046 deg
Day 006 01:28 009 km/hr 094 deg
Day 006 01:29 016 km/hr 093 deg
Day 006 01:30 016 km/hr 092 deg
Day 006 01:31 016 km/hr 093 deg
Day 006 01:32 016 km/hr 093 deg
Day 006 01:33 016 km/hr 080 deg
Day 006 01:34 016 km/hr 343 deg
Day 006 01:35 016 km/hr 343 deg
Day 006 01:36 016 km/hr 343 deg
Day 006 01:37 030 km/hr 342 deg
Day 006 01:38 030 km/hr 341 deg
Day 006 01:39 030 km/hr 295 deg
5. Project Details:
5.1. DIY Anemometer and Vane
The Anemometer was built from repurposed parts. This includes a novel method of creating a one-wire potentiometer using a commutator-brush assembly from an old universal-motor and a DC motor from an old CD-ROM drive.
The wind-direction sensor is based on the commutator which is removed from the old motor assembly. With a hole drilled through the shaft and then 24 1kOhm resistors soldered between commutator segments and one end connected to the shaft. This creates a 24kOhm variable resistor between the brush and grounded shaft which can be used to determine angular position of the wind-direction.
The cup- anemometer is built using plastic balls which have been cut in half and fixed to a central hub using tapped aluminum rods cut from an old clothes-hanger. A small DC motor is driven by the rotating cup-assembly by a thin shaft which passes through the commutator shaft.
This creates a novel concentric assembly integrating anemometer wind-speed measurement using a dc-motor as a generator and commutator variable-resistance as a wind-direction sensor.
Using a simple method the anemometer was calibrated against wind-speed by fixing the assembly on a motor-cycle and noting the values of DC-voltage generated by the small-motor against speed measured on the motor-cycle speedometer.
The images below illustrate the steps in building the DIY anemometer wind-sensor:
Old Motor Assembly | Disassembled Commutator |
Hole in commutator shaft |
Commutator and DC-motor assembly |
Anemometer parts required | Wind-speed calibration |
1 kilo Ohm resistors | Final assembly |
5.2. Electronics
An ‘Anemometer Booster pack’ was designed and fabricated so as to integrate with the the MSP-EXP430FR4133 LaunchPad development kit and the ADS7042 Ultra-Low Power Data Acquisition BoosterPack. The circuit implements the following:
5.2.1. 3.3V LDO based on the TI LM2936
5.2.2. Switched current source for driving one-wire-resistor (commutator with 24, 1 kilo Ohm resistors)
5.2.3. Amplification of the anemometer sensor output (DC-motor output)
5.2.4. Switchable power to the ADS7042 BoosterPack
5.2.5. Switchable ground to the SD-card
5.2.6 Circuit Schematic:
5.2.7. Circuit PCB artwork
| PCB BOTTOM |
5.2.8. Circuit PCB hardware
6. Software Code Listing
/* --COPYRIGHT--,BSD
Uses example code provided by Texas Instruments
Copyright (c) 2014, Texas Instruments Incorporated
All rights reserved.
Arduino Wrapper Function Library for FatFs
(c) 2010, David Sirkin sirkin@stanford.edu
FatFS by ChaN:
http://elm-chan.org/fsw/ff/00index_e.html
--/COPYRIGHT--*/
//******************************************************************************
//
// Road Test on the MSP-EXP430FR4133 LaunchPad development kit and
// the ADS7042 Ultra-Low Power Data Acquisition BoosterPack.
//
// MSP430FR4133 Anemometer Code - LPM3.5, device enter LPM3.5 every 5 minutes
// and captures the value of wind-speed and direction which are stored
// on an SD-Card
//
// Description: Device enter LPM3.5 after configuring the RTC. The RTC wakes
// the device up from LPM3.5 every 5 minutes and increments a counter.
// It stores the values of counter, Day, Hour and Min in the Backup RAM Registers.
//
// It integrates the TI ADS7042 BoosterPack and DIY Anemometer BoosterPack
//
// Wind-speed output ofer signal conditioning is fed to Pin8.0 of MSP430FR4133
// Wind-direction output after signal-conditioning is fed to the ADC of ADS7042
//
// Ajoy Raman
// Freelance Engineer
// www.ajoyraman.in
// January 2017
// Built with energia-1.6.10E18
//******************************************************************************
#include <SPI.h>
#include <LCD_Launchpad.h>
#include <pfatfs.h>
#include <pffconf.h>
#include <msp430.h>
#define sd_card_cs_pin 9 // SD-Card chip select pin
//#define booster_pack_cs_pin 12 // ADS7042 Booster pack chip select pin
#define extAIN 6 // BoosterPack A_IN Pin8.0
LCD_LAUNCHPAD LCD;
volatile unsigned int *myCount = &BAKMEM0; //memory location myCount
volatile unsigned int *Days = &BAKMEM1; //memory location day
volatile unsigned char *Hours = &BAKMEM2_L; //memory location hour
volatile unsigned char *Minutes = &BAKMEM2_H; //memory location minute
unsigned short int bw;//bytes written
int rc; // relates to the status of the file operation
DIR dir; /* Directory object */
FILINFO fno; /* File information object */
uint32_t ReadTemp;
uint32_t ReadTemp1;
uint8_t StringLength = 0, StringLength1 = 0;
char buf[60];
volatile uint32_t counter = 0;
uint32_t AccStringLength = 0;
uint16_t ADCresult; // ADC result of Booster-pack ADC
uint32_t ADCresult1;
uint8_t RXMSB, RXLSB; //Rx MSB, Rx LSB results of Booster-pack ADC
uint8_t Day, Hour, Min, Speed;
void setup() //Does setup() and then goes to loop()
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
FRCTL0 = FRCTLPW | NWAITS_1;
// Set FRAM wait states, necessary for MCLK > 8
initGpio();
setup32kHzOsc();
setupClock(); //16MHz
setupSPI(); //required when we add ADS7402
LCD.init(); // Initialize the LCD
setupUART(); // initialize the serial terminal
analogReference(DEFAULT);
__delay_cycles(1600000);
}
/*-----------------------------------------------------------------------*/
/* Program Main */
/*-----------------------------------------------------------------------*/
void loop()
{
// First determine whether we are coming out of an LPMx.5 or a regular RESET.
if (SYSRSTIV == SYSRSTIV_LPM5WU) {
// When woken up from LPM3.5, reinitialize
//update time variables for print
Day = *Days;
Hour = *Hours;
Min = *Minutes;
counter = * myCount;//restore counter value from memory
// At this time both Current-Source and Booster-Pack power is ON
// Read AIN on MSP430FR4133
__delay_cycles(1600000);
ReadTemp = analogRead(extAIN); //first reading may be in error
__delay_cycles(1600000);
ReadTemp = analogRead(extAIN);
__delay_cycles(1600000);
sprintf( buf, "x%05X ", counter);
LCD.displayText(buf);
//---------------Writing to SD-Card
// carry out the ADS7042 reads during this time as it works
//only within pfatfs when pfatfs is used
P8OUT |= BIT3;//SD Card GND ON
__delay_cycles(1600000);
//Serial.println("FatFs.begining");
FatFs.begin(sd_card_cs_pin); // initialize FatFS library calls
//Serial.println("FatFs.opening");
//"Opening log file to write temperature(LOG.txt)."
__delay_cycles(1600000);
rc = FatFs.open("LOG.TXT");
if (rc) die(rc);
__delay_cycles(1600000);
//Serial.println("FatFs.open");
//Read ADS7042 values
Read_ADS7042_ADC();//first reading may be in error
__delay_cycles(1600000);
Read_ADS7042_ADC();
__delay_cycles(1600000);
Read_ADS7042_ADC();
__delay_cycles(1600000);
//send_BP_ADC_result();
P1OUT |= BIT3; //Switch off afer ADC Conversion OFF
P5OUT |= BIT0; //Current source ckt OFF
ReadTemp = ReadTemp * 3600 / 687;
ReadTemp1 = ReadTemp / 10;
ADCresult1 = ADCresult;
ADCresult1 = (ADCresult1 * 6000) / 1872;//2080; //~ 64/256 >>2
ADCresult1 = ADCresult1 / 100;
ADCresult = ADCresult1;
sprintf( buf, "%02d %03d", ADCresult, ReadTemp1);
LCD.displayText(buf);
sprintf( buf, "Day %03d %02d:%02d %03d km/hr %03d deg ",
Day , Hour, Min, ADCresult, ReadTemp1 );
Serial.println(buf); // write to uart
__delay_cycles(1600000);
bw = 0;
StringLength = strlen(buf); //required for SD Card file write
//SD Card open write and close file
AccStringLength = (counter - 1) * 512;
//FAT format writes are in blocks of 512
rc = FatFs.lseek( AccStringLength );
//moves to the position where we should write
if (rc) die(rc);
rc = FatFs.write(buf, StringLength, &bw);
//write contents of buf with length StringLength
if (rc) die(rc);
rc = FatFs.write(0, 0, &bw); //Finalize write
if (rc) die(rc);
rc = FatFs.close(); //Close file
if (rc) die(rc);
Serial.println("File Closed");
P8OUT &= ~ BIT3; //SD card off
// -- end of SD-Card write -------------------------------
// Store counter value in backup memory register before enter LPM3.5
* myCount = counter;
incrementTime(); //increment day hour minute count
__enable_interrupt(); // The RTC interrupt should trigger now...
}
else {
*Minutes = 0; // Set initial time
*Hours = 0;
*Days = 1;
// Configure RTC
// Initialize RTC to use XT1 and enable RTC interrupt
// Interrupt and reset happen every 1024/32768 * 32 = 1 sec.
RTCMOD = 9600 - 1; //300 sec
//RTCMOD = 512 - 1; //16 sec
//RTCMOD = 32-1; //1 sec
RTCCTL = RTCSS__XT1CLK | RTCSR | RTCPS__1024;
RTCCTL |= RTCIE;
// Store counter value in backup memory register before enter LPM3.5
counter = 0;
* myCount = counter;
//print opening message
LCD.displayText("AJ RDT ");
Serial.print("MSPFR4311 Anemometer Logger Starting \n\r");
__delay_cycles(1600000);
}
// Enter LPM3.5 mode with interrupts enabled. Note that this operation does
// not return. The LPM3.5 will exit through a RESET event, resulting in a
// re-start of the code.
PMMCTL0_H = PMMPW_H; // Open PMM Registers for write
PMMCTL0_L |= PMMREGOFF_L; // and set PMMREGOFF
__bis_SR_register(LPM3_bits | GIE); // Re-enter LPM3.5
__no_operation(); // For debugger
}
//ISR
void __attribute__ ((interrupt(RTC_VECTOR))) RTC_ISR (void) {
switch (RTCIV)
{
case (0) : break;//No Interrupt
case (2) : // RTC Overflow
counter = * myCount;
P4OUT |= BIT0; //for debug
counter++;
// Store check value in backup memory register
* myCount = counter;
break;
default: break;
}
}
/* Stop with dying message */
void die ( int pff_err )
{
Serial.println();
Serial.print("Failed with rc=");
Serial.print(pff_err, DEC);
for (;;) ; //infinite loop
}
void initGpio(void)
{
P1DIR = 0xFF; P2DIR = 0xFF; P3DIR = 0xFF; P4DIR = 0xFF;
P5DIR = 0xFF; P6DIR = 0xFF; P7DIR = 0xFF; P8DIR = 0xFF;
P1REN = 0xFF; P2REN = 0xFF; P3REN = 0xFF; P4REN = 0xFF;
P5REN = 0xFF; P6REN = 0xFF; P7REN = 0xFF; P8REN = 0xFF;
P1OUT = 0x00; P2OUT = 0x00; P3OUT = 0x00; P4OUT = 0x00;
P5OUT = 0x00; P6OUT = 0x00; P7OUT = 0x00; P8OUT = 0x00;
// All are defined as outputs and LOW
//Booster Pack LED1 & LED2 off as invered logic
P2OUT |= BIT7; // LED1
P1OUT |= BIT5; // LED2
//Used to control the ground line to SD Card
P8DIR |= BIT3;
P8OUT &= ~ BIT3; //OFF
//Used to control the Booster pack LDO
P2DIR |= BIT5;
P2OUT &= ~ BIT5; //OFF
//Ext ADCIN input
P8DIR &= ~ BIT0; //Configured as input
//Used to give power to the ADS7042 Booster Pack
P1DIR |= BIT3;//Configured as output
//drives base of NPN transistor power ON when Low
P1OUT &= ~ BIT3; //ON
//P1OUT |= BIT3; //OFF
//Used to give power to the Current source circuit
P5DIR |= BIT0;//Configured as output
//drives base of NPN transistor power ON when Low
P5OUT &= ~ BIT0; //ON
// Disable the GPIO power-on default high-impedance mode
PM5CTL0 &= ~LOCKLPM5;// to activate previously configured port settings
}
void setupClock() {
// Set MCLK = SMCLK = DCOCLKDIV = 16 MHz
// Set ACLK = REF0CLK = ~32 kHz
__bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source
CSCTL0 = 0; // clear DCO and MOD registers
CSCTL1 &= ~(DCORSEL_7); // Clear DCO frequency select bits first
CSCTL1 |= DCORSEL_5; // Set DCO = 16MHz
CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz
__delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL
while (CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // FLL locked
CSCTL4 |= SELMS__DCOCLKDIV | SELA__REFOCLK;
// set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
// default DCOCLKDIV as MCLK and SMCLK source
}
void setup32kHzOsc() {
// Initialize XT1 32kHz crystal
P4SEL0 |= BIT1 | BIT2; // set XT1 pin as second function
do
{
CSCTL7 &= ~(XT1OFFG | DCOFFG); // Clear XT1 and DCO fault flag
SFRIFG1 &= ~OFIFG;
} while (SFRIFG1 & OFIFG); // Test oscillator fault flag
}
void setupUART() {
//--------------------------- Configure UART pins -----------------------------------------------------
P1SEL0 |= BIT0 | BIT1; // I/O Function Selection
// Configure UART for backchannel UART data logging
UCA0CTLW0 |= UCSWRST;
UCA0CTLW0 |= UCSSEL__SMCLK;
// Baud Rate calculation 9600 Baud ------------------------ 9600 Baud ------------------------------
// 16000000/(16*9600) = 104.167
// Fractional portion = 0.167
// User's Guide Table 14-4: UCBRSx = 0xD6
// UCBRFx = int ( (104.167-104)*16) = 2
UCA0BR0 = 104; // 16000000/16/9600
UCA0BR1 = 0x00;
UCA0MCTLW = 0xD600 | UCOS16 | UCBRF_2;
UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI (Enhanced Universal Serial Communication Interface)
}
void setupSPI() {
// Initialize SPI for 4 MHz SCLK, used to communicate with the ADS7042
//pinMode( booster_pack_cs_pin, OUTPUT);
P5SEL0 |= BIT1 | BIT2 | BIT3; // select secondary mode for pins
UCB0CTLW0 |= UCSWRST; // **Put state machine in reset**
UCB0CTLW0 |= UCMST | UCSYNC | UCMSB | UC7BIT; // 3-pin, 8-bit SPI master
UCB0CTLW0 |= UCSSEL__SMCLK; // MODCLK
UCB0BR0 = 4; // /2,fBitClock = fBRCLK/(UCBRx+1).
UCB0BR1 = 0; //
UCB0CTLW0 &= ~UCSWRST; // **Initialize USCI state machine
}
// Routine reads values from booster pack ADC
void Read_ADS7042_ADC()
{
P1OUT &= ~BIT4; // PIN 12 Drive CS pin low to begin SPI communication
//P1.4 directly could have used CS #define CS 12
__delay_cycles(160000);
UCB0TXBUF = 0x00; // Send empty byte to ADS7042
while (!(UCB0IFG & UCRXIFG)); // Wait for RXBUF to receive byte
RXMSB = UCB0RXBUF; // Place byte in RXMSB
UCB0TXBUF = 0x00; // Send empty byte to ADS7042
while (!(UCB0IFG & UCRXIFG)); // Wait for RXBUF to receive byte
RXLSB = UCB0RXBUF; // Place byte in RXLSB
P1OUT |= BIT4; // Return CS pin high, done with ADS7042 SPI for now
//ADCresult = (RXMSB << 7) | RXLSB; //TI exapmle program
ADCresult = (RXMSB << 6) | (RXLSB >> 2);//Changed to work with pFatFs SPI
//ADCresult &= 0x00FF; //kills any 1's in result above 12bit
}
// Time Function
void incrementTime(void)
{
// Deal with minutes increment every interrupt at 5 minute interval
//(*Minutes) = (++(*Minutes) % 60); //60
(*Minutes) = ( ((*Minutes)+5) % 60); //60
if ((*Minutes) == 0)
{
(*Hours) = (++(*Hours) % 24); //24
if ((*Hours) == 0)
(*Days)++;
}
}
// -----------------Used during development for debug-----------------
void send_BP_ADC_result() {
//------------Send Booster Pack ADC result on back channel----------------------
ADCresult = (RXMSB << 7) | RXLSB; // Send ADS7042 result via backchannel UART
ADCresult &= 0x0FFF; //kills any 1's in result above 12bit
//sendASCII((ADCresult >> 12)); // Send upper most nibble
sendASCII((ADCresult >> 8)); // Send upper nibble
sendASCII((ADCresult >> 4)); // Send middle nibble
sendASCII(ADCresult); // Send lower nibble
while (!(UCA0IFG & UCTXIFG)); UCA0TXBUF = 10; // End line
}
void send_LP_ADC_result() {
//------------Send Launch pad ADC result on back channel----------------------
sendASCII((ReadTemp >> 8)); // Send upper nibble
sendASCII((ReadTemp >> 4)); // Send middle nibble
sendASCII(ReadTemp ); // Send lower nibble
while (!(UCA0IFG & UCTXIFG)); UCA0TXBUF = 10; // End of line character
}
// Hex to ASCII sendASCII subfunction
// takes a hex value and turns it into its ASCII representation
// then sends the number to the host via backchannel UART
void sendASCII(int hexvalue)
{
hexvalue &= 0x000F;
if (hexvalue <= 9) {
while (!(UCA0IFG & UCTXIFG));
UCA0TXBUF = hexvalue + 48;
}
else if (hexvalue <= 15) {
while (!(UCA0IFG & UCTXIFG));
UCA0TXBUF = hexvalue + 55;
}
}
7. Summary
It was an great learning experience to carry out this RoadTest. I was able to work with all the IDE tools provided by TI including the CCS Cloud and CCS6 downloaded to my PC. The example and demo programs formed an excellent starting point for software development. The low power modes were fully exploited and it was impressive to see only 100uA in LPM3.5 which increased to only 300uA when the low quiescent current LDO was added. The major current contributor in the active period within LPM 3.5 is the SD-Card which needs 30mA for write operations.Integrating the SD-Card pFatFs library was a major challenge and some interaction was noticed with the SPI c-code of the ADS7042. A work around was found for this. I worked mostly in the Energia-18 work-space as I found it easy to integrate the library functions of Energia with the other c-routines which needed to be added. The hardware provide has been very stable over several programming cycles.
Finally I must acknowledge TI and Element-14 for giving me an opportunity to carry out this RoadTest.
Top Comments
Hi,
Super review with this project!
Thanks
Gerald
---
Brilliant project and description - one of (if not the) the best ever !
I've had a couple of cheapish commercial weather stations and they have not communicated well with a PC and the outdoor bits haven…