Posts with «embedded» label

Adafruit AVRProg Grows UPDI Interface Support

Making a small number of things with an embedded application is pretty straightforward, you usually simply plug in a programmer or debugger dongle (such as an AVRISP2) into your board with an appropriate adaptor cable, load your code into whatever IDE tool is appropriate for the device and hit the program button. But when you scale up a bit to hundreds or thousands of units, this way of working just won’t cut it. Add in any functional or defect-oriented testing you need, and you’re going to need a custom programming rig.

Adafruit have a fair bit of experience with building embedded boards and dealing with the appropriate testing and programming, and now they’ve updated their AVR Programming library to support the latest devices which have moved to the UPDI (Unified Programming and Debug Interface) programming interface. UPDI is a single-wire bidirectional asynchronous serial interface which enables programming and debugging of embedded applications on slew of the new AVR branded devices from Microchip. An example would be the AVR128DAxx which this scribe has been tinkering with lately because it is cheap, has excellent capacitive touch support, and is available in a prototype-friendly 28-pin SOIC package, making it easy peasy to solder.

The library is intended for use with the Arduino platform, so it should run on a vast array of hardware, without any special requirements, so making a custom programming jig out of hardware lots of us have lying around is not a huge hassle.

Adafruit provide a few application examples in the project GitHub to get you going, such as this ATTiny817 example that wipes the flash memory, sets appropriate fuses and drops in a bootloader.

The UPDI code was taken from the [brandanlane’s] portaprog which is hosted on the TTGO T-Display ESP32 board from Chinese outfit LilyGo, which is also worth checking out.

A little while ago we saw how the AVR Multitool, the AVRGPP learned to speak UPDI, and since we’re on programming interfaces, its possible to get the cheap-as-chips USBasp to speak TPI as well.

 

Hack a Day 08 Nov 16:30

Review: OSEPP STEM Kit 1, a Beginner’s All-in-One Board Found in the Discount Aisle

As the name implies, the OSEP STEM board is an embedded project board primarily aimed at education. You use jumper wires to connect components and a visual block coding language to make it go.

I have fond memories of kits from companies like Radio Shack that had dozens of parts on a board, with spring terminals to connect them with jumper wires. Advertised with clickbait titles like “200 in 1”, you’d get a book showing how to wire the parts to make a radio, or an alarm, or a light blinker, or whatever.

The STEM Kit 1 is sort of a modern arduino-powered version of these kits. The board hosts a stand-alone Arduino UNO clone (included with the kit) and also has a host of things you might want to hook to it. Things like the speakers and stepper motors have drivers on board so you can easily drive them from the arduino. You get a bunch of jumper wires to make the connections, too. Most things that need to be connected to something permanently (like ground) are prewired on the PCB. The other connections use a single pin. You can see this arrangement with the three rotary pots which have a single pin next to the label (“POT1”, etc.).

I’m a sucker for a sale, so when I saw a local store had OSEPP’s STEM board for about $30, I had to pick one up. The suggested price for these boards is $150, but most of the time I see them listed for about $100. At the deeply discounted price I couldn’t resist checking it out.

So does an embedded many-in-one project kit like this one live up to that legacy? I spent some time with the board. Bottom line, if you can find a deal on the price I think it’s worth it. At full price, perhaps not. Join me after the break as I walk through what the OSEPP has to offer.

What’s Onboard?

There are plenty of input and output devices:

  • 7 Push Buttons
  • Potentiometers (3 rotary and 1 slide)
  • Passive Infrared Sensor (PIR)
  • Light Sensor
  • Sound Sensor
  • LM35 Temperature Sensor
  • 10 LEDs (various colors)
  • Servo Motor
  • Stepper Motor
  • DC Motor
  • LCD Display
  • Buzzer
  • Speaker
  • RGB LED

In addition, the kit comes with an ultrasonic distance sensor in a little bracket that can connect to the stepper motor. That’s the only part that needs power and ground that isn’t already wired up.

Because the heart of the board is an Arduino UNO clone, you can do anything you like to program it. However, OSEPP touts their visual block diagram language that is basically Scratch. You can use it for free on most platforms and there is even a Web-based version although it can’t download code. It looks like Scratch or other block-oriented systems you’ve seen before.

I’m not usually fond of the visual block languages, but this one at least shows you the actual Arduino code it generates, so that isn’t bad. But you can still use any other method you like such as the standard IDE or PlatformIO.

You can see a video about the board, below.

The Good and the Bad

The board feels substantial and able to withstand a good bit of abuse. There’s a good range of components, and I like that the arduino is a real daughter board and not just built onto the PCB. Despite using the block language, I do like the tutorial booklet. It is very slick and has projects ranging from an IR doorbell to a mini piano. You can see a page below — very colorful and clear.

Of course, the suggested retail price of $150 is a bit offputting. You might think a breadboard with a handful of LEDs and other parts would be a much lower-cost option but just look around for arduino kits for beginners and you’ll find prices are all over the place. On the other hand, with a parts kit you would have to know how to wire up things like stepper motors or DC motors, so there is some value to having it already done for you. There’s also value in not having a bag of parts to misplace.

The jumper wires in the kit have pins on one side and sockets on the other. The pins go into the Arduino’s connector and the sockets go over pins on the components. These aren’t quite as reliable as a spring clip and not as versatile either.

In my mind the worst part of the kit design is that the pins are right next to each of the components. That’s good for understanding, but it makes a mess of wiring. For instance, there are ten LEDs, and connecting them all means stretching jumper wires to both edges of the board The jumpers aren’t very long either, so any complex project is going to have wires crisscrossing the sensors and LCD.

Granted, in this image I could have removed some of the wires from the bundles but that wouldn’t help that much, either. If you need to hook up more than a few of the available components you will have a mess. I would have put some sort of spring clip or even screw terminals and put them all on the top and bottom of the board with clear color-coded marking about where they connect. Then the wiring would all be out of the way. There are probably a few other ways they could have gone, and at this price, they could afford the few extra inches on the PCB.

There are a few other things that would have been nice touches to finish off this kit. I would have enjoyed a short chapter in the booklet about using the Arduino IDE directly so that people know it exists. And having even a small breadboard attached for your own exploration would make sense, but would then call for a different type of jumper wire.

Short Example Using the Distance Sensor

I wanted to do something with the board so I decided to play with the distance sensor and the servo. The distance sensor is a bit annoying both because you have to wire it all up and it has a tendency to fall off when you transport the board.

The demo (you can find it online) won’t win any originality prizes. The program moves the servo to scan from 0 to 180 degrees in 5 degree increments. It measures the distance of what’s in front of it. When it completes a scan, if it saw something close (you could adjust the sensitivity), it moves the sensor back to that position and waits 30 seconds. Otherwise, it keeps scanning.

Really, this is no different from any other Arduino program. That’s kind of the point. Despite the emphasis in the book on the point-and-click language, this is really just an Arduino.

In Summary

For the deep sale price I found, the board will work well for its intended audience of students or anyone starting out with Arduino or microcontrollers. Even a more advanced audience who just wants a way to hammer out a quick prototype might find it worth the $30 or $40 you can sometimes pay. But at full price, it is hard to imagine this makes sense because of the mess of wire routing and limited expansion options.

Machine Learning on Tiny Platforms Like Raspberry Pi and Arduino

Machine learning is starting to come online in all kinds of arenas lately, and the trend is likely to continue for the forseeable future. What was once only available for operators of supercomputers has found use among anyone with a reasonably powerful desktop computer. The downsizing isn’t stopping there, though, as Microsoft is pushing development of machine learning for embedded systems now.

The Embedded Learning Library (ELL) is a set of tools for allowing Arduinos, Raspberry Pis, and the like to take advantage of machine learning algorithms despite their small size and reduced capability. Microsoft intended this library to be useful for anyone, and has examples available for things like computer vision, audio keyword recognition, and a small handful of other implementations. The library should be expandable to any application where machine learning would be beneficial for a small embedded system, though, so it’s not limited to these example applications.

There is one small speed bump to running a machine learning algorithm on your Raspberry Pi, though. The high processor load tends to cause small SoCs to overheat. But adding a heatsink and fan is something we’ve certainly seen before. Don’t let your lack of a supercomputer keep you from exploring machine learning if you see a benefit to it, and if you need more power than just one Raspberry Pi you can always build a cluster to get your task done just a little bit faster, too.

Thanks to [Baldpower] for the tip!

Wi-Fi Body Scale with Arduino

In this post we present the design of a scale that connects to the Internet and automatically sends weight info on a Google Document.

The project is composed of

 

Hardware

Taking a look at the diagram, we can distinguish three sections:

one for handling digital inputs (two buttons and scale’s switch),
the LCD manager
the analog signal acquisition from load cells


You may notice that P1 and P2 buttons are respectively managed by Arduino’s A3 and A4 inputs (configured as digital), while the INT switch reads the A5 signal (digital as well).

The LCD display used in our project belongs to the family of those based on HD44780 chipset, equipped with seven-pin control (RW, D4, D5, D6, D7, RS and Enable). As you can see from the diagram, RW is permanently connected to ground so it’s only operating in writing mode.

D4, D5, D6 and D7 pins are respectively managed by 3, 5, 6 and 7 digital pins of the Arduino Uno; RS is managed by pin 8 and the Enable pin from pin A0 (this configuration will also be specified in the software).

Besides the power supply (a VCC connected to 5V) and the ground (GND), the display requires a level of voltage between 0 and 5 volts to adjust the contrast in the input pin VO (adjustment made through R6 potentiometer).

Finally the display has 2 pin (A and K respectively anode and cathode) for switching on the backlight: feeding the pin with a voltage of 5V, the backlight is turned on, otherwise it is off.

The switching on and off is handled via a push-button switch that connects or disconnects the ground from the cathode.

Finally let’s analyze the part that manages the analog signal from the load cells: this is done by INA125 integrated component, a high precision amplifier developed specifically for measuring tools like scales.


The IC is fitted with two pins (6 and 7) representing the input of the differential amplification stage. These pins are connected to the two ends of the Wheatstone bridge (which in our project correspond to two of the load cell pins).

As you can figure out, the differential signal coming from the bridge has an absolute value of just a few millivolts: our project requires an amplification of about 500 times, obtained with a 120 ohm resistor.

The amplified output is provided at pins 10 and 11 of INA125; these pins are connected to A1 analog of the Arduino board, which therefore will be used for reading the value ADC.

R1: 39 ohm
R2: 1 kohm
R3: 4,7 kohm
R4: 4,7 kohm
R5: 330 ohm
R6: Trimmer 10 kohm
R7: 4,7 kohm

C1: 100 nF 63 VL
C2: 100 µF 25 VL

U1: INA125

P1: Microswitch
P2: Microswitch

LCD: Display LCD 8×2

 

Hacking  the Velleman scale

Our system is designed to be matched to the high-capacity scale produced by Velleman. But you can using any scale usign 4 load cells. The original Velleman electronics and display must be phased out and replaced with our project.

The scale structure is made up of 4 load cells (arranged on the 4 corners), which are configured between them so as to create a single Wheatstone bridge.

To make the connection to our board you must disassemble the lower section of the scale in order to access the electronics.

Referring to Fig A, which shows the structure of the scale viewed from below, with a quick visual analysis is easy to figure out that some blue and red wires are connected to each other while the yellow wires are connected to the switches positioned on the front of the two cells that identify the presence of a person.


To hack the scale follow these steps:


  • unsolder or cut all the red and blue wires, push in short those that were already shorted previously,

  • unsolder or cut the yellow wires coming from the cells numbered 1 and 3, put them in parallel (possibly by welding), extend them and take them to the two terminals on the new INT printed board (Fig.B)

  • unsolder or cut the white wire that is referred to as G3 (Wheatstone bridge mass) on the original printed circuit, extend take it to terminal 1 on the new printed circuit;

  • unsolder or cut the white wire that is referred to as G2 (Wheatstone bridge positive) on the original printed circuit, extend take it to terminal 3 on the new printed circuit;

  • unsolder or cut the white wire that is referred to as G1 and G4 on the original printed circuit, extend take it to terminal 4 and 2 on the new printed circuit (differential signal on the Wheatstone bridge)

 


The Sketch

As for the LCD, Arduino provides a convenient library (<LiquidCrystal.h>) already supporting different kinds of LCD (both 8 and 16 chars for 2 lines) based on the parallel interface chipset HD44780.

After the inclusion of the library, you must initialize the connection to the display, through LiquidCrystal LCDDisplay (8, A0, 3, 5, 6, 7), in this command, the parameters passed to the function and indicate Arduino’s hardware pins used to connect RS, Enable, D4, D5, D6 and D7 pins of the display.

Depending on the Arduino setup could be necessary to execute LCDDisplay.begin (width, height) in which the parameters indicate the physical dimensions of the display: LCDDisplay.begin (8, 2) in this case.

The library makes available instructions:

  • LCDDisplay.setCursor (x, y) to position the cursor in a certain position
  • LCDDisplay.print (String) to print strings


Management of Arduino’s EEPROM is done by the <EEPROM.h> library  that, once included, provides the functions:


  • EEPROM.write (add, date) to write a byte in a particular cell and

  • EEPROM.read (add) that the instead reads a byte from one cell (the add parameter specifies the address of the EEPROM cell).


Wi-Fi network and internet connection are managed thanks to the <WiServer.h> library; you just need to define some variables to configure the network (like IP’s and Network masks).


The adapter to connect and publish data on Google Documents shall operate as a Web Client: this configuration is done in the setup instruction WiServer.init (NULL) in which the NULL parameter specifies client mode.


You must create a function that will receive and handle the response from the server, this is done by the googlePublish.setReturnFunc  (Gestione_Risposte_Web) instruction  in our example. This function must be defined later in the code: in our example is void Gestione_Risposte_Web (char * data, int len) and receives as parameters both a pointer to a string containing the response and the length (in bytes) of the response.


Sending information requires to make a POST request to Google’s servers: WiServer library makes available a POSTrequest just to do this.


You must define the IP address of the server to which you want to connect (Google in our case) and define a structure of the POSTrequest (in our case, the variable is called googlePublish) containing the IP address of the server, the TCP / IP port the server name (spreadsheets.google.com), URL of POST execution and finally a function (SearchQuery in our case) that builds POST request body.

 

/******************************************************
 BilanciaWiFi
 Autori: Ingg. Tommaso Giusto e Ing. Alessandro Giusto
 Email:  tommro@libero.it
******************************************************/

// Inclusione Libreria per Display LCD 8x2
#include <LiquidCrystal.h>
// Inclusione Libreria per EEPROM scheda
#include <EEPROM.h>
// Inclusione Libreria per Server Web WiFi
#include <WiServer.h>
// Inclusione Libreria per SD Card
//#include <SD.h>

// Definizione pin INPUT/OUTPUT
const int PinVPeso = A1;         // Ingresso analogico uscita peso
const int PinPulsanteP1 = A3;    // Ingresso pulsante P1 su A3
const int PinPulsanteP2 = A4;    // Ingresso pulsante P2 su A4
const int PinInterruttore = A5;  // Ingresso interruttore su A5

// Definizione/Inizializzazione PIN Display LCD
// LCD RS pin D8
// LCD Enable on pin A0
// LCD D4, D5, D6, D7 on pins D3, D5, D6, D7
// LCD R/W pin a massa
// LCD V0 pin trimmer tra +5V e massa
LiquidCrystal LCDDisplay (8, A0, 3, 5, 6, 7);

/******************************************************
 Definizione struttura EEPROM
******************************************************/

/*****************************************************/
// Byte 0x000        Nome
// ...               Utente
// Byte 0x01F        000 (0x00 Carattere fine stringa)
// ...               
// ...               
// ...               
// Byte 0x120        Nome
// ...               Utente
// Byte 0x13F        009 (0x00 Carattere fine stringa)
/*****************************************************/
#define StartNomiUtentiEEPROMADD          0x0000
#define EndNomiUtentiEEPROMADD            0x013F
#define DimensioneNomeUtenteEEPROMADD     32
#define NumMaxNomiUtenti                  10

/*****************************************************/
// Byte 0x140        Parte Alta Peso Utente 000
// Byte 0x141        Parte Bassa Peso Utente 000
// ...               
// ...               
// ...               
// Byte 0x152        Parte Alta Peso Utente 009
// Byte 0x153        Parte Bassa Peso Utente 009
/*****************************************************/
#define StartPesiUtentiEEPROMADD          0x0140
#define EndPesiUtentiEEPROMADD            0x0153
#define DimensionePesoUtenteEEPROMADD     2

#define PesoMIN                           100
#define PesoMAX                           2000
#define AnalogDifferenzaPesoMAX           15

#define CalibrazioneSensoreMIN            0
#define CalibrazioneSensoreMAX            80

/******************************************************
 Definizione variabili globali
******************************************************/

// Definizione Parametri Rete Wireless
#define WIRELESS_MODE_INFRA	1  // Infrastrutturata (basata su Access Point)
#define WIRELESS_MODE_ADHOC	2  // Ad-hoc (senza Access Point)

// Parametri di rete
unsigned char local_ip[] = {192, 168, 0, 89};      // Indirizzo IP
unsigned char gateway_ip[] = {192, 168, 0, 254};	    // Indirizzo gateway IP
unsigned char subnet_mask[] = {255, 255, 255, 0};   // Subnet Mask
const prog_char ssid[] PROGMEM = {"FlashMob"};	    // SSID access point

// Selezione tipo di cifratura rete Wireless
unsigned char security_type = 0;  // 0 -> nessuna cifratura
                                  // 1 -> cifratura WEP
                                  // 2 -> cifratura WPA
                                  // 3 -> cifratura WPA2

// Password cifratura per WPA/WPA2 (max. 64 cratteri)
const prog_char security_passphrase[] PROGMEM = {"12345678"};

// Password cifratura per WEP 128-bit keys
prog_uchar wep_keys[] PROGMEM = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
				 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// Selezione tipo di rete Wireless infrastrutturata
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

// Variabili per lunghezza SSID e password di cifratura
unsigned char ssid_len;
unsigned char security_passphrase_len;

// Definizione Parametri Pubblicazione Google
// Indirizzo IP per server www.google.it
uint8 google_ip[] = {209, 85, 229, 101};

// Richiesta POST verso GOOGLE
POSTrequest googlePublish (google_ip, 80, "spreadsheets.google.com", "", searchQuery);

// Stringa per eseguire pubblicazione
char newURL[] = {"/formResponse?formkey=dDFPcWhxNlZxMEpnUnhNdE5fT1lDcWc6MQ&ifq&entry.0.single=++++++++++++++++++++++++++++++++&entry.1.single=999,9&submit=Submit"};

// This function generates the body of our POST request
void searchQuery() {
}

// Indica presenza SD Card
//boolean presenzaSDCard;

// Inizializzazione Scheda
void setup() {
  // Inizializzo dimensioni LCD (8x2)
  LCDDisplay.begin (8, 2);

  // Inizializzo pin usati come INPUT/OUTPUT
  pinMode (PinInterruttore, INPUT);  
  pinMode (PinPulsanteP1, INPUT);
  pinMode (PinPulsanteP2, INPUT);

  // Se all'accensione rilevati tutti e 2 i pulsanti premuti
  if ((PulsantePremuto (PinPulsanteP1) == 1) &&
      (PulsantePremuto (PinPulsanteP2) == 1)) {
    // Segnalo inizializzazione in corso
    LCDDisplay.setCursor (0, 0);
    LCDDisplay.print ("Init EEP");
    LCDDisplay.setCursor (0, 1);
    LCDDisplay.print ("in corso");

    // Inizializzo EEPROM scheda
    InizializzaEEPROM();
    // Attesa
    delay (500);
  }
  // Inizializzo porta seriale
  Serial.begin (9600);  
  Serial.println ("Bilancia WiFi");
  Serial.println ("By Ingg. Tommaso e Alessandro Giusto");
  // Invio stato utenti
  InviaProgrammazioneUtenti();

  // Segnalo Avvio Web
  LCDDisplay.setCursor (0, 0);
  LCDDisplay.print ("  Init  ");
  LCDDisplay.setCursor (0, 1);
  LCDDisplay.print ("  web   ");  

  // Inizializzo WiServer
  WiServer.init (NULL);
  WiServer.enableVerboseMode (false);

  // Inizializzazione richiesta POST (parametro indica funzione a cui verra' passata la risposta)
  googlePublish.setReturnFunc (Gestione_Risposte_Web);    

/*
  // Inizializzo SD Card
  pinMode(10, OUTPUT);  
  presenzaSDCard = SD.begin (4);
  // Se inizializzazione SD fallita  
  if (presenzaSDCard == false)
    Serial.println ("Init SD FAULT!");
  // Se inizializzazione SD riuscita
  else
    Serial.println ("Init SD OK!");
*/
}

// Programma Principale
void loop() {
  // Ciclo infinito di esecuzione
  for (;;) {
    // Gestione Peso
    GestionePeso();

    // Per circa 1 sec
    for (byte tmp = 0; tmp < 250; tmp++) {
      // Gestione programmazione seriale
      GestioneSeriale();
      // Gestione WiServer
      GestioneWiServer();
      // Attesa
      delay (4);
    }    // Chiusura ciclo for per circa 1 sec
  }    // Chiusura ciclo infinito di esecuzione
}

/******************************************************
 Definizione funzioni
******************************************************/

// Funzione Gestione Peso
void GestionePeso() {
  // Indice utente selezionato
  //    0,..,(NumMaxNomiUtenti-1) -> selezionato utente 1,..,NumMaxNomiUtenti
  //    0xFF -> nessun utente selezionato
  static int IndiceUtenteSelezionato = 0xFF;
  // Indica l'indice carattere visualizzato  
  static int IndiceCarattere = 0;
  String NomeUtenteSelezionato;
  int PesoUtenteSelezionato;
  int LunghezzaNomeUtenteSelezionato;
  int ValoreSensorePeso1, ValoreSensorePeso2;
  int ValoreSensorePesoTMP1, ValoreSensorePesoTMP2, ValoreSensorePesoTMP3, ValoreSensorePesoTMP4;
  static int CalibrazioneSensore;
  int CalibrazioneSensoreTMP;
  int PesoCalcolato;
  int tmp;

  // Se non premuto interruttore (utente non salito)
  if (PulsantePremuto (PinInterruttore) == 0) {
    // Leggo valore sensore per calibrazione
    CalibrazioneSensoreTMP = analogRead (PinVPeso);
    // Se valore sensore per calibrazione valido
    if ((CalibrazioneSensoreTMP >= (int) (CalibrazioneSensoreMIN)) &&
      (CalibrazioneSensoreTMP <= (int) (CalibrazioneSensoreMAX)))
      // Aggiorno valore sensore per calibrazione
      CalibrazioneSensore = CalibrazioneSensoreTMP;
  }    // Chiusura if non premuto interruttore (utente non salito)

  // Se premuto tasto 1
  if (PulsantePremuto (PinPulsanteP1) == 1) {
    // Azzero indice carattere visualizzato
    IndiceCarattere = 0;

    // Verifico tutte le posizioni utente
    for (tmp = 0; tmp < NumMaxNomiUtenti; tmp++) {
      // Seleziono utente selezionato
      switch (IndiceUtenteSelezionato) {
        // Se selezionato ultimo utente/nessun utente selezionato
        case (NumMaxNomiUtenti - 1): case 0xFF:
          // Seleziono primo utente
          IndiceUtenteSelezionato = 0;
          break;    // Chiusura case selezionato ultimo utente/nessun utente selezionato

        // Se selezionato altro utente
        default:
          // Seleziono prossimo utente
          IndiceUtenteSelezionato++;
          break;    // Chiusura case nessun utente selezionato
      }    // Chiusura switch seleziono utente selezionato

      // Se trovato utente con nome non nullo
      if (LeggiNomeUtente (IndiceUtenteSelezionato).length() != 0x00)
        // Blocco ciclo for verifico tutte le posizioni utente
        break;
    }    // Chiusura ciclo for verifico tutte le posizioni utente

    // Se non trovato utente con nome non nullo
    if (tmp == NumMaxNomiUtenti)
      // Seleziono nessun utente
      IndiceUtenteSelezionato = 0xFF;
  }    // Chiusura if premuto tasto 1

  // Se utente selezionato
  if (IndiceUtenteSelezionato != 0xFF) {
    // Leggo nome/peso utente
    NomeUtenteSelezionato = LeggiNomeUtente (IndiceUtenteSelezionato);
    PesoUtenteSelezionato = LeggiPesoUtente (IndiceUtenteSelezionato);
    // Calcolo lunghezza nome utente    
    LunghezzaNomeUtenteSelezionato = NomeUtenteSelezionato.length();
    // Seleziono lunghezza nome utente
    switch (LunghezzaNomeUtenteSelezionato) {
      case 0:
        // Indico nessun utente selezionato
        IndiceUtenteSelezionato = 0xFF;
        break;
      case 1: case 2: case 3: case 4: case 5: case 6: case 7:
        LCDDisplay.clear();
        LCDDisplay.setCursor ((8 - LunghezzaNomeUtenteSelezionato) / 2, 0);
        LCDDisplay.print (NomeUtenteSelezionato);
        break;
      case 8:
        LCDDisplay.setCursor (0, 0);
        LCDDisplay.print (NomeUtenteSelezionato);
        break;
      default:
        if ((IndiceCarattere + 8) > LunghezzaNomeUtenteSelezionato)
          IndiceCarattere = 0;

        LCDDisplay.setCursor (0, 0);
        LCDDisplay.print (&NomeUtenteSelezionato[IndiceCarattere]);

        // Messaggio scorrevole
        IndiceCarattere++;
        if ((IndiceCarattere + 8) > LunghezzaNomeUtenteSelezionato)
          IndiceCarattere = 0;
        break;
    }    // Chiusura switch seleziono lunghezza nome utente
    LCDDisplay.setCursor (0, 1);
    LCDDisplay.print (PesoUtenteSelezionato / 1000);
    LCDDisplay.print ((PesoUtenteSelezionato % 1000) / 100);
    LCDDisplay.print ((PesoUtenteSelezionato % 100) / 10);
    LCDDisplay.print (".");
    LCDDisplay.print (PesoUtenteSelezionato % 10);    
    LCDDisplay.print (" Kg");

    // Se premuto interruttore (utente salito)
    if (PulsantePremuto (PinInterruttore) == 1) {
      LCDDisplay.setCursor (0, 1);
      LCDDisplay.print ("..... Kg");

      // Leggo valore analogico peso
      for (;;) {
        // Attesa
        delay (500);
        // Eseguo prima lettura peso
        ValoreSensorePesoTMP1 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP2 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP3 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP4 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePeso1 = ((ValoreSensorePesoTMP1 + ValoreSensorePesoTMP2 + ValoreSensorePesoTMP3 + ValoreSensorePesoTMP4) / 4);

        // Attesa
        delay (500);
        // Eseguo seconda lettura peso
        ValoreSensorePesoTMP1 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP2 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP3 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePesoTMP4 = analogRead (PinVPeso);
        delay (125);
        ValoreSensorePeso2 = ((ValoreSensorePesoTMP1 + ValoreSensorePesoTMP2 + ValoreSensorePesoTMP3 + ValoreSensorePesoTMP4) / 4);

        // Se peso stabilizzato
        if ((ValoreSensorePeso1 <= ValoreSensorePeso2) && ((ValoreSensorePeso2 - ValoreSensorePeso1) < (int) (AnalogDifferenzaPesoMAX)) ||
            (ValoreSensorePeso1 > ValoreSensorePeso2) && ((ValoreSensorePeso1 - ValoreSensorePeso2) < (int) (AnalogDifferenzaPesoMAX)))
          // Blocco ciclo for leggo valore analogico peso
          break;
      }    // Chiusura ciclo for leggo valore analogico peso

      // Calcolo peso utente
      PesoCalcolato = (int) (ValoreSensorePeso2 - CalibrazioneSensore);
      PesoCalcolato = (PesoCalcolato + (((int) (PesoCalcolato * (int) (10))) / (int) (62)));

//    68.1 KG
//    642   con calibrazione: 62 -> Veff = 580

      // Se peso valido
      if ((PesoCalcolato >= (int) (PesoMIN)) && (PesoCalcolato <= (int) (PesoMAX))) {
        // Memorizzo peso utente
        ScriviPesoUtente (IndiceUtenteSelezionato, PesoCalcolato);

        // Visualizzo peso calcolato
        LCDDisplay.setCursor (0, 1);
        LCDDisplay.print (PesoCalcolato / 1000);
        LCDDisplay.print ((PesoCalcolato % 1000) / 100);
        LCDDisplay.print ((PesoCalcolato % 100) / 10);
        LCDDisplay.print (".");
        LCDDisplay.print (PesoCalcolato % 10);    
        LCDDisplay.print (" Kg");

        // Richiedo pubblicazione        
        LCDDisplay.setCursor (0, 0);
        LCDDisplay.print ("Pubblic?");

        // Per massimo 5 secondi attendo pressione tasto 2
        for (tmp = 0; tmp < 10; tmp++) {
          // Se premuto tasto 2
          if (PulsantePremuto (PinPulsanteP2) == 1) {
            // Indico pubblicazione in corso     
            LCDDisplay.setCursor (0, 0);
            LCDDisplay.print ("Pubblic.");
            LCDDisplay.setCursor (0, 1);
            LCDDisplay.print ("  wait  ");

            // Eseguo pubblicazione
            for (tmp = 0; tmp < NomeUtenteSelezionato.length(); tmp++) {
              newURL[76 + tmp] = NomeUtenteSelezionato.charAt(tmp);
              if (newURL[76 + tmp] == ' ')
                newURL[76 + tmp] = '+';
            }
            newURL[124] = ((PesoCalcolato / 1000) + 0x30);
            newURL[125] = (((PesoCalcolato % 1000) / 100) + 0x30);      
            newURL[126] = (((PesoCalcolato % 100) / 10) + 0x30);      
            newURL[128] = ((PesoCalcolato % 10) + 0x30);      
            googlePublish.setURL(newURL);
            googlePublish.submit();    
            delay (500);
            // Blocco ciclo for
            break;
          }
          delay (500);          
        }    // Chiusura ciclo for per massimo 5 secondi attendo pressione tasto 2
/*
        // Se SD rilevata
        if (presenzaSDCard == true) {
          // Verifico/Creo directory BilanciaWiFi
          if (!(SD.exists ("BilanciaWiFi")))
            SD.mkdir ("BilanciaWiFi");

          // Memorizzo il peso su file
          File filePeso;
          // Apro il file in scrittura
          char nomeFilePeso[50] = {"BilanciaWiFi/"};
          for (tmp = 0; tmp < NomeUtenteSelezionato.length(); tmp++) {
            charTmp[0] = NomeUtenteSelezionato.charAt(tmp);
            strcat (nomeFilePeso, charTmp);
          }
          strcat (nomeFilePeso, ".txt");          
          filePeso = SD.open (nomeFilePeso, FILE_WRITE);

          // Se apertura file OK
          if (filePeso) {
            // Memorizzo il peso
            filePeso.print ((PesoCalcolato / 1000) + 0x30);
            filePeso.print (((PesoCalcolato % 1000) / 100) + 0x30);
            filePeso.print (((PesoCalcolato % 100) / 10) + 0x30);
            filePeso.print (".");
            filePeso.print ((PesoCalcolato % 10) + 0x30);
            filePeso.print (" Kg");
            // Chiudo il file:
            filePeso.close();
          }
        }
*/

        LCDDisplay.setCursor (0, 0);
        LCDDisplay.print ("  Step  ");
        LCDDisplay.setCursor (0, 1);
        LCDDisplay.print ("  off   ");

        // Attendo utente scende dalla bilancia
        for (;;) {
          // Se non premuto interruttore (utente non salito)
          if (PulsantePremuto (PinInterruttore) == 0)
            break;
        }
      }    // Chiusura if peso valido
    }    // Chiusura if premuto interruttore (utente salito)
  }    // Chiusura if utente selezionato

  // Se nessun utente selezionato
  else {
    LCDDisplay.setCursor (0, 0);
    LCDDisplay.print (" Chose  ");
    LCDDisplay.setCursor (0, 1);
    LCDDisplay.print (" user   ");
  }    // Chiusura if nessun utente selezionato
}

// Funzione Gestione Seriale
void GestioneSeriale() {
  String comandoRicevutoString = "";
  int numeroUtenteRicevuto;
  String nomeUtenteRicevuto = "";

  // Se ricevuti dati dalla porta seriale
  if (Serial.available()) {
    // Attendo tutti i dati
    delay (250);

    // Ricevo i dati
    while (Serial.available())
      comandoRicevutoString += (char) (Serial.read());

    // Invio comando ricevuto    
    Serial.println (comandoRicevutoString);

    // Se ricevuti almeno 15 caratteri
    if (comandoRicevutoString.length() >= 15) {
      // Se ricevuto comando programmazione nome utente (NOME_UTENTE_xx=)USER_NAME_01=Boris
      if ((comandoRicevutoString.substring (0, 12).equals("P_USER_NAME_")) &&
          (isdigit(comandoRicevutoString.charAt (12))) &&
          (isdigit(comandoRicevutoString.charAt (13)))) {
        // Estraggo numero utente ricevuto
        numeroUtenteRicevuto = (((comandoRicevutoString.charAt (12) - 0x30) * 10) +
                                (comandoRicevutoString.charAt (13) - 0x30));
        // Se ricevuto numero utente corretto
        if ((numeroUtenteRicevuto >= 1) && (numeroUtenteRicevuto <= NumMaxNomiUtenti)) {
          // Estraggo nome utente
          nomeUtenteRicevuto = comandoRicevutoString.substring (15);
          // Se nome troppo lungo
          if (nomeUtenteRicevuto.length() > DimensioneNomeUtenteEEPROMADD)
            // Tronco il nome
            nomeUtenteRicevuto = nomeUtenteRicevuto.substring (0, DimensioneNomeUtenteEEPROMADD);

          // Memorizzo il nome
          ScriviNomeUtente (numeroUtenteRicevuto - 1, nomeUtenteRicevuto);
          // Azzero il relativo peso
          AzzeraPesoUtente (numeroUtenteRicevuto - 1);
          // Indico OK
          Serial.println ("OK");
          // Invio su  porta seriale lo stato utenti
          InviaProgrammazioneUtenti();
          // Termino funzione
          return;
        }
      }
    }  

    // Indico errore
    Serial.println ("FAULT");
    // Termino funzione
    return;
  }
}

// Invia su  porta seriale lo stato utenti
void InviaProgrammazioneUtenti() {
  for (int numeroUtente = 0; numeroUtente < NumMaxNomiUtenti; numeroUtente++) {
    Serial.print ("Us. ");
    Serial.print ((numeroUtente + 1) / 10);
    Serial.print ((numeroUtente + 1) % 10);
    Serial.print (": ");
    Serial.println (LeggiNomeUtente (numeroUtente));
  }
}

// Funzione Gestione WiServer
void GestioneWiServer() {
  // Gestione WiServer
  WiServer.server_task();
}

// Gestione diverse risposte provenienti dal WEB
void Gestione_Risposte_Web (char* data, int len) {

  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and 
  // includes the HTTP header. 
  while (len-- > 0) {
    Serial.print(*(data++));
  } 

}

// Scrive il nome di un utente
// INPUT:   Posizione utente (0,..,9)
//          Nome utente in formato stringa
// OUTPUT:  -
// Note:    -
void ScriviNomeUtente (int NomePosition, String NomeUtente) {
  // Azzero nome utente selezionato
  AzzeraNomeUtente (NomePosition);

  // Scrivo i caratteri nome utente selezionato
  for (int tmp = 0; tmp < NomeUtente.length(); tmp++)
    EEPROM.write (StartNomiUtentiEEPROMADD + tmp +
                  (NomePosition * DimensioneNomeUtenteEEPROMADD), NomeUtente.charAt(tmp));
}

// Legge il nome di un utente
// INPUT:   Posizione utente (0,..,9)
// OUTPUT:  Nome utente in formato stringa
// Note:    -
String LeggiNomeUtente (int NomePosition) {
  String NomeUtente = "";
  char NomeUtenteChar;

  // Leggo i caratteri nome utente selezionato
  for (int tmp = 0; tmp < DimensioneNomeUtenteEEPROMADD; tmp++) {
    // Leggo singolo carattere
    NomeUtenteChar = EEPROM.read (StartNomiUtentiEEPROMADD + tmp +
                                  (NomePosition * DimensioneNomeUtenteEEPROMADD));

    // Se trovato carattere fine nome
    if (NomeUtenteChar == 0x00)
      // Blocco ciclo for leggo i caratteri nome utente selezionato
      break;

    // Accodo carattere letto
    NomeUtente = NomeUtente + NomeUtenteChar;
  }

  // Ritorno il nome
  return (NomeUtente);
}

// Azzera il nome di un utente
// INPUT:   Posizione utente (0,..,9)
// OUTPUT:  -
// Note:    -
void AzzeraNomeUtente (int NomePosition) {
  // Azzero caratteri nome utente selezionato
  for (int tmp = 0; tmp < DimensioneNomeUtenteEEPROMADD; tmp++)
    EEPROM.write (StartNomiUtentiEEPROMADD + tmp +
                  (NomePosition * DimensioneNomeUtenteEEPROMADD), 0); 
}

// Scrive il peso di un utente
// INPUT:   Posizione utente (0,..,9)
//          Peso utente
// OUTPUT:  -
// Note:    -
void ScriviPesoUtente (int PesoPosition, int PesoUtente) {
  // Memorizzo peso nome utente
  EEPROM.write (StartPesiUtentiEEPROMADD + 
                (PesoPosition * DimensionePesoUtenteEEPROMADD), (byte) (PesoUtente >> 8));
  EEPROM.write (StartPesiUtentiEEPROMADD + 1 +
                (PesoPosition * DimensionePesoUtenteEEPROMADD), (byte) (PesoUtente & 0x00FF));
}

// Legge il peso di un utente
// INPUT:   Posizione utente (0,..,9)
// OUTPUT:  Peso utente
// Note:    -
int LeggiPesoUtente (int PesoPosition) {
  // Ritorno il peso
  return (((int) (EEPROM.read (StartPesiUtentiEEPROMADD +
                               (PesoPosition * DimensionePesoUtenteEEPROMADD))) << 8) +
           (int) (EEPROM.read (StartPesiUtentiEEPROMADD + 1 +
                               (PesoPosition * DimensionePesoUtenteEEPROMADD))));
}

// Azzera il peso di un utente
// INPUT:   Posizione utente (0,..,9)
// OUTPUT:  -
// Note:    -
void AzzeraPesoUtente (int PesoPosition) {
  // Azzero peso nome utente
  ScriviPesoUtente (PesoPosition, 0);
}

// Inizializza EEPROM scheda
// INPUT:   -
// OUTPUT:  -
// Note:    -
void InizializzaEEPROM() {
  // Azzero i nomi/pesi di tutti gli utenti
  for (int tmp = 0; tmp < NumMaxNomiUtenti; tmp++) {
    AzzeraNomeUtente (tmp);
    AzzeraPesoUtente (tmp);
  }    
}

// Verifica lo stato di un pulsante
// INPUT:   Pin pulsante
// OUTPUT:  Stato pulsante    0 -> pulsante non premuto
//                            1 -> pulsante premuto
// Note:    -
int PulsantePremuto (int PinPulsante) {
  if (digitalRead (PinPulsante) == LOW)
    return (1);
  else
    return (0);
}

 

Install the WiShield libraries


To work properly, the Wifi shield requires specific libraries that must be installed in the Arduino IDE used to compile the sources so you’ll need to put these libraries in the Arduino libraries path.


The library supports multiple operating modes (APP_WEBSERVER, APP_WEBCLIENT, APP_SOCKAPP, APP_UDPAPP and APP_WISERVER).

The default value is APP_WEBSERVER, which runs on most Arduino systems but has several limitations. The main limitation is that it can’t function both as a client and server simultaneously.


It is therefore recommended the APP_WISERVER (in the library named “WiServer.h“) which allows the Arduino shield equipped with Wi-Fi to be configured and operated either as a Web Server as a Web Client.


In the first case, you can serve connection requests from external web clients and send HTML pages in response, in the second it will be possible to connect to a web server and send GET or POST requests.


To configure the WiShield library as APP_WISERVER you must open the apps-conf.h file, comment the “#define“ APP_WEBSERVER row and uncomment the “#define” of APP_WISERVER row.


Something like:


/ / # define APP_WEBSERVER

/ / # define APP_WEBCLIENT

/ / # define APP_SOCKAPP

/ / # define APP_UDPAPP

# defineAPP_WISERVER


Configuring publishing

Performing data publication on Google Documents requires three distinct steps:


  1. Configuring Wi-Fi network access;
  2. Creating a Google Docs form module: each module is characterized by an identifying 34 characters string – FormKey) and each field is characterized by an identification number (called Entry).
  3. Inserting the FormKey / Entry in the Arduino software.



Configuring network parameters

First you must configure and update the IP address, Subnet Mask, Gateway IP and Network SSID, then select the encryption mode and change password information for your network. Save all, compile and run the upload Software tab.

Creating the Google  module doc

Create a form on Google Docs, modify the form with two fields (username and weight, both of type string) and save the job. (the result is something like the one shown in Figure). In our case, we left the default file name and called the two fields “User Name” and “User weight”

take note of this key string because then it will be inserted in the Arduino software: as you can see from the picture, the link shows the form key assigned to the module (in this example, the link is: https://docs.google.com/spreadsheet/viewform?formkey=dDFPcWhxNlZxMEpnUnhNdE5fT1lDcWc6MQ which implies that the form assigned key is “dDFPcWhxNlZxMEpnUnhNdE5fT1lDcWc6MQ”);

 

Clicking on the link will open a new page that shows the data entry form: within this form text fields are marked with “entry.0.single“, “entry.1.single“, etc … (basically with an integer id)

 To see which number has been assigned to our fields you need to display the HTML source of the form (the result is something like the one shown in this Figure).

You can easily find the ID numbers assigned to different fields (in our example “entry.0.single” is the “Name” and “entry.1.single” is the “weight“).

Take note of this information because it will be included in the source Arduino as well.

Formkey / entry

Once you have created the form, the Arduino source will be configured so that it is compatible with the form you created.

A variable is defined in Arduino source named newURL[]: the variable is initialized with a standard format then modified to perform publication (refer to Figure 11 to identify it in the code).


Replace the default values with those obtained at the time of the creation of the form, compile and download it to the Arduino board.

 

 

 

How to program user names

The configuration of user names can be made via serial port and Serial Monitor. To program it, connect to the board (via USB cable) and open the Arduino Serial Monitor. As a first step the card sends a summary of the current configuration.


To program a new user name you must send a string in the format “P_USER_NAME_xx = User Name” with xx between 01 and 10 ID (put an empty string to delete the selected user.) If all the posted information is correct, this is confirmed by the message “Ok” otherwise “Fault”.

 

Using the scale


The scale software is able to distinguish up to 10 users (each identified by its name) and, for each one, it stores the last measured weight.


If you want to clear the memory (which we recommend at first use) is sufficient to hold down either of the two buttons (the display indicates the operation with “EEP init in progress”).

Later, you can configure names following the procedure described in the “Programming user names” section.


As the next step the Web management interface will start and the system will enter its normal operation. The user selection is done by pressing button P1.


Going over the scale starts the weighing procedure (lasting several seconds): weight is displayed and stored and the scale asks for publishing on the web: by pressing P2 the operation is performed.

How to connect a weather station WS2355 (or WS2300) to Weather Underground with Arduino

Our weather station LIVE

 

There are several softwares that enable the publication of the weather data of a professional weather station, but they all work on PC, so we should connect the weather station to a computer and leave it on, the idea is not good because the computer takes up space and consumes a lot. At a time when we should spare the energy, its not a good thing.
The project we’re talking about instead operates as a stand-alone application and allows you to publish the weather station data independently on http://www.wunderground.com, “forgetting” the PC consumption and even the desk space.

Arduino controls the dialogue with the weather station to acquire the data and also the Ethernet interface to transfer them, by making the necessary connection to the Internet via ADSL, passing by a router pointing to the IP address of the Weather Underground site and transferring information using the TCP / IP.

 

Our project

This circuit reads data from a weather station with serial interface and upload the data collected on http://www.wunderground.com.
Not all stations are equal, therefore, our circuit can not be universal but it is indicated specifically for stations La Crosse WS2355, WS2300 or WS2350.

The choice of weather station WS23xx was dictated primarily by a consideration: it has a convenient serial interface to connect and dialogue with a microcontroller such as Arduino.
We chose www.wunderground.com site because it allows anyone to upload their weather data, but also to monitor the temperature, humidity, pressure, wind etc. on line. Also on this site there are many widgets that allow you to integrate data in website or blog.
An example is the Widget for the home page of Google.
The data sent by users are publicly available, then from the home page we can specify the geographic area that interests us and we will see a screen that lists all of the corresponding stations.

How does it work?

The system has two circuit: one is the famous Arduino UNO and the other is a shield that integrates the Ethernet module WIZnet (you may, of course, use the the original Ethernet Shield), as well as a TTL/RS232 logic level converter MAX238 type. The converter allows you to adapt the serial levels of weather station to the TTL levels of Arduino.
Please be aware that the communication port of the weather station is not a simple RS232, because it has no ground reference. The DTR line must have a negative voltage while the RTS must be positive. The lack of common ground leads to the hypothesis (remember that there is no official document) that these two lines are taken as a reference of logic levels used in communication. It is a system that not only allows the use of serial connections relatively long, but also gives the opportunity to agree devices with different voltage levels on the serial port.
To communicate with La Crosse, the speed of the serial device (in our case, the Arduino module) must be set to 2,400 bps, with blocks of 8 bits, no parity and one stop bit (2400-8-N-1).
The memory map of the weather station, like many other unofficial information on the WS2355 is on the site http://www.lavrsen.dk/foswiki/bin/view/Open2300/WebHome.

 

The hardware

The jumpers on the lines T3IN and R3OUT (respectively RS232/TTL converter output and RS232/TTL converter input) let you choose which lines use for Arduino communication: we give you the opportunity to choose if use the serial hardware TXD and RXD or the two I/O digital D2 and D3 emulating a serial port with the appropriate library that can be downloaded from the website http://arduiniana.org/libraries/newsoftserial.

 

R1: 470 ohm
C1: 1 µF 100 VL
C2: 1 µF 100 VL
C3: 1 µF 100 VL
C4: 1 µF 100 VL
U1: WIZNET
U2: MAX238CNG
LD1: led 3 mm red

- socket 12+12
- Strip male 3 via (2 pz.)
- Jumper (2 pz.)
- Strip female/male 6 via (2 pz.)
- Strip female/male 8 via(2 pz.)
- Strip female 10 via (4 pz.)
- DB9 male
- PCB


 

 

The sketch

/*

****************************************************************
*  Name    : Personal Weather Station to wunderground.com      *
*  Author  : Landoni Boris                                     *
*  www.open-electronics.org                                    *
*  blog.elettronicain.it                                       *
*  www.futurashop.it                                           *
****************************************************************

http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol#Example_URL

action [action=updateraw] -- always supply this parameter to indicate you are making a weather observation upload
ID [ID as registered by wunderground.com]
PASSWORD [PASSWORD registered with this ID, case sensative]
dateutc - [YYYY-MM-DD HH:MM:SS (mysql format)] In Universal Coordinated Time (UTC) Not local time
winddir - [0-360 instantaneous wind direction]
windspeedmph - [mph instantaneous wind speed]
humidity - [% outdoor humidity 0-100%]
tempf - [F outdoor temperature]
rainin - [rain inches over the past hour)] -- the accumulated rainfall in the past 60 min
indoortempf - [F indoor temperature F]
indoorhumidity - [% indoor humidity 0-100]
softwaretype - [text] ie: WeatherLink, VWS, WeatherDisplay

windgustmph - [mph current wind gust, using software specific time period]
windgustdir - [0-360 using software specific time period]
windspdmph_avg2m  - [mph 2 minute average wind speed mph]
winddir_avg2m - [0-360 2 minute average wind direction]
windgustmph_10m - [mph past 10 minutes wind gust mph ]
dewptf- [F outdoor dewpoint F]
dailyrainin - [rain inches so far today in local time]
baromin - [barometric pressure inches]
weather - [text] -- metar style (+RA)
clouds - [text] -- SKC, FEW, SCT, BKN, OVC
soiltempf - [F soil temperature]
 * for sensors 2,3,4 use soiltemp2f, soiltemp3f, and soiltemp4f
soilmoisture - [%]
* for sensors 2,3,4 use soilmoisture2, soilmoisture3, and soilmoisture4
leafwetness  - [%]
+ for sensor 2 use leafwetness2
solarradiation - [W/m^2]
UV - [index]
visibility - [nm visibility]

http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?ID=KCASANFR5&PASSWORD=XXXXXX&dateutc=2000-01-01+10%3A32%3A35&winddir=230&windspeedmph=12&windgustmph=12&tempf=70&rainin=0&baromin=29.1&dewptf=68.2&humidity=90&weather=&clouds=&softwaretype=vws%20versionxx&action=updateraw

mappa ws2300

Command	Hex Digit	  	Command	Hex Digit
82	 0	  	 A2	 8
86	 1	  	 A6	 9
8A	 2	  	 AA	 A
8E	 3	  	 AE	 B
92	 4	  	 B2	 C
96	 5	  	 B6	 D
9A	 6	  	 BA	 E
9E	 7	  	 BE	 F

*/

#include <SPI.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include <NewSoftSerial.h>

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,0,145 };
byte server[] = { 38,102,136,125 }; // http://weatherstation.wunderground.com/

#define UPDATE_INTERVAL            300000    // if the connection is good wait 60 seconds before updating again - should not be less than 5
unsigned long update=0;

NewSoftSerial mySerial(2, 3);   //NewSoftSerial mySerial(rx,tx);

const int ledPin = 6; // the pin that the LED is attached to
const int rts = 4;
const int dtr = 5;
String id = "xxxxxxx";
String PASSWORD = "xxxxxxx";
boolean upload=1;
boolean leggicontinuo=0;

Client client(server, 80);

void setup() {
  Ethernet.begin(mac, ip);

  digitalWrite(ledPin, LOW);
  // initialize serial communication:
  Serial.begin(2400);
  Serial.println("Goodmorning WS2300!");
  // set the data rate for the NewSoftSerial port
  mySerial.begin(2400);

  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  pinMode(rts, OUTPUT);
  pinMode(dtr, OUTPUT);

  digitalWrite(rts, HIGH);
  digitalWrite(dtr, LOW);
  delay(2000);
  digitalWrite(rts, LOW);
  digitalWrite(dtr, HIGH);

  for (int i=0;i<10;i++){
    digitalWrite(ledPin, HIGH);
    delay(200);
    digitalWrite(ledPin, LOW);
    delay(200);
  }

}

void loop() {

  int incomingByteSer;      // a variable to read incoming serial data into
  // see if there's incoming serial data:
  if (Serial.available() > 0) {
    // read the oldest byte in the serial buffer:
    incomingByteSer = Serial.read();
    // if it's a capital H (ASCII 72), turn on the LED:
    Serial.flush();
    if (incomingByteSer == 'o') {
      String data=getTime();
      Serial.print("ora ");
      Serial.println(data);
    }  

    if (incomingByteSer == 'd') {
      String data=getDay();
      Serial.print("giorno ");
      Serial.println(data);
    }
    if (incomingByteSer == 't') {
      String data=getTemp(1);
      Serial.print("temperatura interna F ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'T') {
      String data=getTemp(0);
      Serial.print("temperatura esterna F ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'h') {
      String data=getHum(1);
      Serial.print("umidità interna ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'H') {
      String data=getHum(0);
      Serial.print("umidità esterna ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'p') {
      String data=getPress(1);
      Serial.print("pressione hPa ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'P') {
      String data=getPress(0);
      Serial.print("pressione Hg ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'w') {
      String data=getWind(0);
      Serial.print("wind speed mph ");
      Serial.println(data);     

    } 

    if (incomingByteSer == 'W') {
      String data=getWind(1);
      Serial.print("wind dir ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'r') {
      String data=getRain(1);
      Serial.print("rain 1 ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'R') {
      String data=getRain(0);
      Serial.print("rain 24 ");
      Serial.println(data);
    }  

    if (incomingByteSer == 'e') {
      String data=getDew();
      Serial.print("dew point ");
      Serial.println(data);
    } 

    if (incomingByteSer == 'U') {
      readws();
    } 

    if (incomingByteSer == 'u') {
      pubblica();
    }

    if (incomingByteSer == 'a') {
      if (upload==1){
        upload=0;
        Serial.println("Disattivo l'upload automatico ");
      }
      else
      {
        upload=1;
        Serial.println("Attivo l'upload automatico ");
      }
    }

    if (incomingByteSer == 'A') {
      if (leggicontinuo==1){
        leggicontinuo=0;
        Serial.println("Disattivo lettura continua ");
      }
      else
      {
        leggicontinuo=1;
        Serial.println("Attivo lettura continua ");
      }
    }

    // if it's an L (ASCII 76) turn off the LED:
    if (incomingByteSer == 'L') {
      digitalWrite(ledPin, LOW);
    }
  }

  if (upload==1){
    if (millis() < update) update = millis();
      if ((millis()% 1000) < 2){
        delay (100);
        Serial.print(".");
      }
        if ((millis() - update) > UPDATE_INTERVAL){
            update = millis();
            readws();
            pubblica();
            Serial.println("tempo impiegato per fare la pubblicazione: ");
            Serial.println(millis()-update);
        }
  }

  if (leggicontinuo==1){
    if (millis() < update) update = millis();
      if ((millis()% 1000) < 2){
        delay (100);
        Serial.print(".");
      }
        if ((millis() - update) > UPDATE_INTERVAL){
            update = millis();
            readws();
        }
  }

}

void pubblica()
{

  int timeout=0;
  int skip=0;
  String inString="";
  digitalWrite(ledPin, HIGH);
  Serial.print("connecting  ");

  if (client.connect()) {

    Serial.println("connected");
    //client.println("GET / HTTP/1.0");
    Serial.print("GET /weatherstation/updateweatherstation.php?");//modificare qua.
    client.print("GET /weatherstation/updateweatherstation.php?");//modificare qua.
    pubbws();
    client.println(" HTTP/1.0");
    Serial.println(" HTTP/1.0");

    Serial.print("HOST: ");
    client.print("HOST: ");
    client.println("http://www.wunderground.com");
    Serial.println("http://www.wunderground.com");
    client.println();
  } else {
    Serial.println("connection failed");
  }
  while (!client.available() && timeout<50)
  {
    timeout++;
    Serial.print("Time out ");
    Serial.println(timeout);
    delay(100);
  }

  while (client.available())
  {
        char c = client.read();
          if ((inString.length())<150){ inString.concat(c);}
  }         

  client.flush();

      if ((inString.length())>5)
      {
              Serial.print("Risposta ");
              Serial.print(inString);
      }              

   if (!client.connected())
   {
        Serial.println("disconnecting.");
        client.stop();
        delay (1000);
   }
   digitalWrite(ledPin, LOW);

}

void readws(){

      Serial.println("URL ");
      Serial.print("http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?");
      String temp =getDay();
      String temp1=getTime();
      if ((temp.length()>6) && (temp1.length()>4)){
        Serial.print("ID=");
        Serial.print(id);
        Serial.print("&PASSWORD=");
        Serial.print(PASSWORD);
        Serial.print("&dateutc=");
        Serial.print(temp);
        Serial.print("+");
        Serial.print(temp1);
        scrivimem(temp,0);
        scrivimem(temp1,20);
      }
      else
      {
        Serial.println("Lettura data/ora non riuscita torno");
        return;
      }
      temp=getWind(1);
      if (temp.length()>1){
        Serial.print("&winddir=");
        Serial.print(temp);
        scrivimem(temp,40);
      }
      temp=getWind(0);
      if (temp.length()>2){
        Serial.print("&windspeedmph=");
        Serial.print(temp);
       scrivimem(temp,60);
      }
      temp=getTemp(0);
      if (temp.length()>3){
        Serial.print("&tempf=");
        Serial.print(temp);
        scrivimem(temp,80);
      }
      temp=getRain(1);
      if (temp.length()>2){
        Serial.print("&rainin=");
        Serial.print(temp);
        scrivimem(temp,100);
      }
      temp=getRain(0);
      if (temp.length()>2){
        Serial.print("&dailyrainin=");
        Serial.print(temp);
        scrivimem(temp,120);
      }
      temp=getHum(0);
      if (temp.length()>1){
        Serial.print("&humidity=");
        Serial.print(temp);
        scrivimem(temp,140);
      }
      temp=getDew();
      if (temp.length()>1){
        Serial.print("&dewptf=");
        Serial.print(temp);
        scrivimem(temp,160);
      }
      temp=getPress(0);
      if (temp.length()>1){
        Serial.print("&baromin=");
        Serial.print(temp);
        scrivimem(temp,180);
      }
}

void pubbws(){
      String temp =leggimem(0);
      String temp1=leggimem(20);
      if ((temp.length()>6) && (temp1.length()>4)){
        Serial.print("ID=");
        Serial.print(id);
        Serial.print("&PASSWORD=");
        Serial.print(PASSWORD);
        Serial.print("&dateutc=");
        Serial.print(temp);
        Serial.print("+");
        Serial.print(temp1);
        client.print("ID=");
        client.print(id);
        client.print("&PASSWORD=");
        client.print(PASSWORD);
        client.print("&dateutc=");
        client.print(temp);
        client.print("+");
        client.print(temp1);
      }
      else
      {
        Serial.println("Lettura data/ora non riuscita torno");
        return;
      }
      temp=leggimem(40);
      if (temp.length()>1){
        Serial.print("&winddir=");
        Serial.print(temp);    

        client.print("&winddir=");
        client.print(temp);
      }    

      temp=leggimem(60);
      if (temp.length()>2){
        Serial.print("&windspeedmph=");
        Serial.print(temp);        

        client.print("&windspeedmph=");
        client.print(temp);
      }   

      temp=leggimem(80);
      if (temp.length()>3){
        Serial.print("&tempf=");
        Serial.print(temp);

        client.print("&tempf=");
        client.print(temp);
      }   

      temp=leggimem(100);
      if (temp.length()>2){
        Serial.print("&rainin=");
        Serial.print(temp);       

        client.print("&rainin=");
        client.print(temp);
      }   

      temp=leggimem(120);
      if (temp.length()>2){
        Serial.print("&dailyrainin=");
        Serial.print(temp);       

        client.print("&dailyrainin=");
        client.print(temp);
      }   

      temp=leggimem(140);
      if (temp.length()>1){
        Serial.print("&humidity=");
        Serial.print(temp);        

        client.print("&humidity=");
        client.print(temp);
      }   

      temp=leggimem(160);
      if (temp.length()>1){
        Serial.print("&dewptf=");
        Serial.print(temp);        

        client.print("&dewptf=");
        client.print(temp);        

      }   

      temp=leggimem(180);
      if (temp.length()>1){
        Serial.print("&baromin=");
        Serial.print(temp);        

        client.print("&baromin=");
        client.print(temp);        

      } 

        Serial.print("&windgustmph=0.00");
        client.print("&windgustmph=0.00");
        Serial.print("&action=updateraw");
        client.print("&action=updateraw");

}

void scrivimem(String dataStr, int pos){

  for (int i=0; i<dataStr.length();i++)
  {
    EEPROM.write((pos+i),dataStr[i]);
  }
  EEPROM.write((pos+dataStr.length()),'#');
  for (int i=pos;i<(pos+50);i++)
  {
    if (EEPROM.read(i)=='#')
    {
      break;
    }
  }

}

String leggimem(int pos){
  int lung=0;
  char dataStr[15];

  for (int i=0;i<15;i++)
  {
    dataStr[i]=0;
  }
  for (int i=pos;i<(pos+20);i++)
  {
    lung++;
    if (EEPROM.read(i)=='#')
    {
      break;
    }
    char c=EEPROM.read(i);
    dataStr[i-pos]=(c); 

  }
  dataStr[lung]='\0';
  return(dataStr);
}

String Leggi (int mem[]) {
    String inString;
    char inChar [3];
    int incomingByte=0;      // a variable to read incoming serial data into
    int time=0;
    String chk;
     while ((incomingByte!=2) && (time<20)){
          time++;
          mySerial.print(byte(06));
          delay(20); 

            if ((mySerial.available() > 0) && (time<20)) {
              delay(30);
              time++;
              incomingByte = mySerial.read();
              inString.concat(incomingByte);
            }
      }

     inString="";
     time=0;
     inString.concat("00"); //aggiungo due cifre
     mySerial.flush();
     if (time>18){
          return("0\0");
     }  

      for (int i=0; i<2; i++){

          if (inString.length()>8){
            chk=(inString.substring(4,8));
          }
          inString="";
          inString.concat("00"); //aggiungo due cifre
          for (int tmp=0; tmp<5; tmp++){
            mySerial.print(byte(mem[tmp]));
            while ((mySerial.available() == 0) && (time<100)){
             delay(30);
             time++;
            }
            if (time>98){
              return("0\0");
              break;  

            }        

            if (mySerial.available() > 0) {
              incomingByte = mySerial.read();
            }

            time=0;
           //delay(200);
               while ((mySerial.available() > 0) && time<200 ) {
                    time++;
                    delay(20);
                    incomingByte = mySerial.read();
                    if (tmp<4){
                      if (tmp!=((incomingByte & B11110000)/B10000)){
                          return("0\0");
                          break;
                      }
                    }
                    if (tmp==4){
                      sprintf(inChar, "%02X", incomingByte);
                      inString.concat(inChar);
                    }
               }
           }

      }
        if (chk!=(inString.substring(4,8)))
        {
          return("0\0");

        }
        inString=inString.trim();
        if (inString.length()>16){
          inString=(inString.substring(0,15));
        }
        return(inString);
}

String getTime(){

      String dataStr="";
      int my_array[] = {0x82,0x8A,0x82,0x82,0xDA};  //0200
      String inString;

        for (int ritenta=0; ritenta<10; ritenta++){
           dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                //data= inString.substring(4,6);  //estraggo i decimali della temperatura
                dataStr.concat(inString.substring(6,8));  //estraggo l'ora
                dataStr.concat(":");
                dataStr.concat(inString.substring(4,6));  //estraggo i minuti
                dataStr.concat(":");
                dataStr.concat(inString.substring(2,4));  //estraggo i secondi
                if (((inString.substring(6,8))<23 && (inString.substring(6,8))>=0)&&((inString.substring(4,6))<60 && (inString.substring(4,6))>=0) && ((inString.substring(2,4))<60 && (inString.substring(2,4))>=0)) {
                    break;
                }
                else {
                  dataStr="";
                }

             }
             else
             {
                 delay(500);
             }
        }
      return(dataStr);
}

String getDay(){
      String inString;
      String dataStr="";
      int my_array[] = {0x82,0x8A,0x8E,0xAE,0xEE};
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                dataStr.concat("20");
                dataStr.concat(inString.charAt(13));
                dataStr.concat(inString.charAt(10));
                dataStr.concat("-");
                dataStr.concat(inString.charAt(11));
                dataStr.concat(inString.charAt(8));
                dataStr.concat("-");
                dataStr.concat(inString.charAt(9));
                dataStr.concat(inString.charAt(6));
                if (((inString[13]<='9' && inString[13]>='0')&&(inString[10]<='9' && inString[10]>='0')) && ((inString[11]<='1' && inString[11]>='0')&&(inString[8]<='9' && inString[8]>='0')) && ((inString[9]<='3' && inString[9]>='0')&&(inString[6]<='9' && inString[6]>='0'))) {
                    break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
               delay(500);
           }
        }
      return(dataStr);
}

String getTemp(int dato){
      String inString;
      String dataStr="";
      //String dataStr;
      char cdata [10];
      int lung=0;
      char buff[10];
      int my_array[6];

      if (dato==1){
        my_array[0] = (0x82);
        my_array[1] = (0x8E);
        my_array[2] = (0x92);
        my_array[3] = (0x9A);
        my_array[4] = (0xFA);
      }
       else
      {
        my_array[0] = (0x82);
        my_array[1] = (0x8E);
        my_array[2] = (0x9E);
        my_array[3] = (0x8E);
        my_array[4] = (0xFA);
      }
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                (inString.substring(4,6)).toCharArray(buff,3);
                dataStr.concat((atoi(buff))-30);
                dataStr.concat(".");
                dataStr.concat(inString.substring(2,4));  //estraggo le decine e unità della temperatura
               if (((inString[4]<='9' && inString[4]>='0')&&(inString[5]<='9' && inString[5]>='0')) && ((inString[2]<='9' && inString[2]>='0')&&(inString[3]<='9' && inString[3]>='0'))) {
                   break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
              delay(500);
           }
        }
      if (dataStr.length()>2)
      {

        dataStr.toCharArray(cdata, dataStr.length()+1);
          float fdata=(atof(cdata)*1.800+32.000);
          if (fdata<1000){lung=7;}
          if (fdata<100){lung=6;}
          if (fdata<10){lung=5;}
          dtostrf(fdata,lung,3,cdata);
          dataStr=cdata;
      }
      else
      {
        dataStr="";
      }

      return(dataStr);
}

String getHum(int dato){
      String dataStr="";
      String inString;
      int my_array[6];
      if (dato==1){
        my_array[0] = (0x82);
        my_array[1] = (0x8E);
        my_array[2] = (0xBE);
        my_array[3] = (0xAE);
        my_array[4] = (0xDA);
      }
       else
      {
        my_array[0] = (0x82);
        my_array[1] = (0x92);
        my_array[2] = (0x86);
        my_array[3] = (0xA6);
        my_array[4] = (0xDA);
      }
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                dataStr.concat(inString.substring(2,4));  //estraggo le decine e unità della temperatura
               if ((inString[2]<='9' && inString[2]>='0')&&(inString[3]<='9' && inString[3]>='0')) {
                   break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
               delay(500);
           }
        }
      return(dataStr);
}

String getPress(int dato){
      String dataStr="";
      String inString;
      int my_array[6];
      if (dato==1){
        my_array[0] = (0x82);
        my_array[1] = (0x96);
        my_array[2] = (0xBA);
        my_array[3] = (0x8A);
        my_array[4] = (0xD6);
      }
       else
      {
        my_array[0] = (0x82);      //press hg
        my_array[1] = (0x96);
        my_array[2] = (0xB6);
        my_array[3] = (0xB6);
        my_array[4] = (0xD6);
      } 

        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                dataStr.concat(inString.charAt(7));
                dataStr.concat(inString.charAt(4));
                dataStr.concat(inString.charAt(5));
                if (dato==1){
                  dataStr.concat(inString.charAt(2));
                  dataStr.concat(".");
                  dataStr.concat(inString.charAt(3));
                }
                else
                {
                  dataStr.concat(".");
                  dataStr.concat(inString.charAt(2));
                  dataStr.concat(inString.charAt(3));
                }

                if ((inString[7]<='9' && inString[7]>='0')&&(inString[4]<='9' && inString[4]>='0') && (inString[5]<='9' && inString[5]>='0')&&(inString[2]<='9' && inString[2]>='0') && (inString[2]<='9' && inString[2]>='0')) {
                  break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
              delay(500);
           }
        }
      return(dataStr);

}

String getWind(int dato){
      String dataStr="";
      char cdata [10];
      int lung=0;
      String inString;
      char buff[10];
      int my_array[] = {0x82,0x96,0x8A,0x9E,0xF2};
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
              if (dato==0){
                 dataStr.concat(inString.charAt(7));
                 buff[0]=inString[4];
                 buff[1]=inString[5];
                 int tmp=(strtol(buff,NULL,16));
                 if (tmp<=9){
                   dataStr.concat("0");
                 }

                 dataStr.concat(tmp); // converts a HEX string to long
                 dataStr.concat(dataStr.charAt(2));
                 dataStr.setCharAt(2, '.');
                if ((inString[4]<='9' && inString[4]>='0')&&(inString[5]<='9' && inString[5]>='0')) {
                  break;
                }
                else {
                  dataStr="";
                }
              }
              else
              {
                 buff[0]=inString[6];
                 buff[1]=0;
                 int tmp=((strtol(buff,NULL,16))*22.5);
                 dataStr.concat(tmp);
                if ((tmp>=0)&&(tmp<=360)) {
                  break;
                }
                else {
                  dataStr="";
                  ritenta=0;
                }
              }
           }
           else
           {
               delay(500);
           }
        }

      if (dato==0){    //se richiedo la velocità convertoin miglia orarie    

           if (dataStr.length()>2)
           {
                  dataStr.toCharArray(cdata, (dataStr.length()+1));
                  float fdata=(atof(cdata)*2.23);
                  if (fdata<1000){lung=6;}
                  if (fdata<100){lung=5;}
                  if (fdata<10){lung=4;}
                  dtostrf(fdata,lung,2,cdata);
                  dataStr=cdata;
          }
          else
          {
            dataStr="";
          }
      }

      return(dataStr);
}

String getRain(int dato){
       String dataStr="";
      char cdata [10];
      int lung=0;
      String inString;
            int my_array[6];
      if (dato==1){      //pioggia 1 ora
        my_array[0] = (0x82);
        my_array[1] = (0x92);
        my_array[2] = (0xAE);
        my_array[3] = (0x92);
        my_array[4] = (0xDA);
      }
       else
      {
        my_array[0] = (0x82);      //0497
        my_array[1] = (0x96);
        my_array[2] = (0xA6);
        my_array[3] = (0x9E);
        my_array[4] = (0xD6);
      }
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){
                dataStr.concat(inString.substring(6,8));
                dataStr.concat(inString.substring(4,6));
                if ((inString[6]<='9' && inString[6]>='0')&&(inString[7]<='9' && inString[7]>='0') && (inString[4]<='9' && inString[4]>='0')&&(inString[5]<='9' && inString[5]>='0') && (inString[2]<='9' && inString[2]>='0')&& (inString[3]<='9' && inString[3]>='0')) {
                  break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
               delay(500);
           }
        }

         if (dataStr.length()>2)
      {  

          dataStr.toCharArray(cdata, (dataStr.length()+1));
            //Serial.print("cdata ");
            //Serial.println(cdata);
          float fdata=(atof(cdata)*2.54);  //in pollici
          if (fdata<1000){lung=6;}
          if (fdata<100){lung=5;}
          if (fdata<10){lung=4;}
          dtostrf(fdata,lung,2,cdata);
          dataStr=cdata;
      }
      else
      {
        dataStr="";
      }

      return(dataStr);
}

String getDew(){

      String inString;
      String dataStr="";
      char cdata [10];
      int lung=0;
      char buff[10];

      //Serial.println("leggo pioggia 1h ");
      int my_array[] = {0x82,0x8E,0xB2,0xBA,0xFA};
        for (int ritenta=0; ritenta<10; ritenta++){
            dataStr="";
            inString=Leggi(my_array);
            if (inString.length()>5){            

                (inString.substring(4,6)).toCharArray(buff,3);
                dataStr.concat((atoi(buff))-30);
                dataStr.concat(".");
                dataStr.concat(inString.substring(2,4));  //estraggo le decine e unità della temperatura
               if (((inString[4]<='9' && inString[4]>='0')&&(inString[5]<='9' && inString[5]>='0')) && ((inString[2]<='9' && inString[2]>='0')&&(inString[3]<='9' && inString[3]>='0'))) {
                   break;
                }
                else {
                  dataStr="";
                }
           }
           else
           {
              delay(500);
           }
        }

      if (dataStr.length()>2)
      {

          dataStr.toCharArray(cdata, dataStr.length()+1);
          float fdata=(atof(cdata)*1.800+32.000);
            if (fdata<1000){lung=7;}
            if (fdata<100){lung=6;}
            if (fdata<10){lung=5;}
            dtostrf(fdata,lung,3,cdata);

          dataStr=cdata;
            }
      else
      {
        dataStr="";
      }

      return(dataStr);
}

For the connection we uses the serial port pins 2 and 3 of module Arduino for this reason we have used the library NewSoftSerial that lets you emulate a UART using the generic contact I/O such as serial lines.
Periodically, the Arduino microcontroller polls the weather station and stores the data for the date, time, outside temperature, outside humidity, wind speed and direction, rain fell in one hour and 24 hours, the atmospheric pressure and dew point. The constant UPDATE_INTERVAL defines after how many seconds Arduino public the data collected on the site www.wunderground.com.
In order to publish the data of the control unit on this site, as already mentioned, you must have an account, once registered, there is assigned an ID that will be introduced during the connection to the site in order to proceed with publication.
In the sketch for the Arduino you must enter your ID and password, so that the publication will be successful, otherwise our system will point to the site but will not get access.
For the publication of data is sufficient to recall the page http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php,
passing data to the URL. For example, you have to compose a string like this:

http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?ID=KCASANFR5&PASSWORD=XXXXXX&dateutc=2000-01-01+10&winddir=230&windspeedmph=12&windgustmph=12&tempf=70&rainin=0&baromin=29.1&dewptf=68.2&humidity=90&weather=&clouds=&softwaretype=vws%20versionxx&action=updateraw.

As you see, the user ID and password are inserted in the string. The data temperature should be expressed in Fahrenheit degrees and the wind speed must be in mph (miles per hour). The conversion of the data read by the control unit is made directly from the sketch, so as to spare you the burden to proceed with the manual calculation.

The use of the site www.wunderground.com for the publication of meteorological data is free and subject only to the rules of the provider, there are also advanced features, including the addition of photos and images from a webcam filming on location is the weather station and sent through the Internet.