venerdì 9 marzo 2012

Orologio con allarme

Altra segnalazione...
questa volta si tratta di un orologio, sempre LCD, con possibilità di settare un allarme...
da tenere presente per quando farò il progettino del timer per la lavatrice!!!


Qua lo sketch modificato (ho rimosso la parte del sensore di temperatura e del riconoscimento dei giorni):

#include <avr/pgmspace.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#define CLOCK_ADDRESS 0x68
#define MESSAGE_LENGTH 17
#define DEBOUNCE 50
#define MAX_ALARM 7
#define BUZZER 13

boolean DaylightSavings = false;
char Message[MESSAGE_LENGTH]    = "                ";
char TopLine[MESSAGE_LENGTH]    = "                ";
char BottomLine[MESSAGE_LENGTH] = "                ";
char SetMessage[]               = "   Clock Set    ";
char AlarmMessage[]             = "   Alarm Set    ";
byte Temp1;
byte Year = 9;
byte Month = 12;
byte Day = 26;
byte DoW = 8;
byte Hour = 3;
byte Minute = 41;
byte Second = 30;
byte oldHour = 255;     // These three are set to absurd values to trigger
byte oldMinute = 255;   // the change-sensitive bits in loop().
byte oldSecond = 255;   //
byte oldDay = 255;      //
boolean OldButtonState = false;
boolean NewButtonState = false;
volatile byte Rotor = 0;
byte UIDelay=100;

// Button/rotary switch connections
byte Button = 10;
byte A = 2;
byte B = 11;

byte AlarmOn = 0;               // lowest bits are flags indicating which alarms are on.
byte AlarmMinute[MAX_ALARM];    // Array of alarm minutes...
byte AlarmHour[MAX_ALARM];      // ...hours...
byte AlarmDays[MAX_ALARM];      // ...and days.
char OnAlarms[8] = "SMTWHFS";   // Indicators for alarm on these days...
char OffAlarms[8] = "smtwhfs";  // ...and off.
boolean AlarmRinging = false;   // Is the alarm sounding?
boolean BuzzerState = false;    // Is the alarm (while sounding) currently on or off?

char *WeekDay[7] = {
        "Sunday          ",
        "Monday          ",
        "Tuesday         ",
        "Wednesday       ",
        "Thursday        ",
        "Friday          ",
        "Saturday        "};

LiquidCrystal lcd(8,9,7,6,5,4);
float tconvert = 500.0/1024.0;  // converts A/D value to temperature.

void SetClock() {
    // This is the user interface routine for setting the clock.
    // The 1307 chip is actually set using SetTime().

    UpdateTimeDisplayPlus(Hour, Minute, Year, DaylightSavings);
    SetBottomLine(DoW, Month, Day);
    writeLCD(0,TopLine);
    writeLCD(1,BottomLine);

    // Here's where we set the hour
    Rotor = Hour + 48;      // The extra value on rotor allows easier turn-back.
    while (!ButtonPressed()) {
        delay(UIDelay);
        Hour = Rotor % 24;
        UpdateTimeDisplayPlus(Hour, Minute, Year, DaylightSavings);
        writeLCD(0,TopLine);
        lcd.setCursor(0,0);
        lcd.cursor();
    }

    // Here we set the minute
    Rotor = Minute + 120;   // The extra value on rotor allows easier turn-back.
    while (!ButtonPressed()) {
        delay(UIDelay);
        Minute = Rotor % 60;
        UpdateTimeDisplayPlus(Hour, Minute, Year, DaylightSavings);
        writeLCD(0,TopLine);
        lcd.setCursor(3,0);
        lcd.cursor();
    }

    // Here we set the year
    Rotor = Year;
    while (!ButtonPressed()) {
        delay(UIDelay);
        Year = Rotor;
        UpdateTimeDisplayPlus(Hour, Minute, Year, DaylightSavings);
        writeLCD(0,TopLine);
        lcd.setCursor(10,0);
        lcd.cursor();
    }

    // Here we set the Daylight Savings status
    // I set the initial value of the rotor to 127 or 128: odd is
    // true, even is false.
    if (DaylightSavings) {
        Rotor = 127;
    } else {
        Rotor = 128;
    }
    while (!ButtonPressed()) {
        delay(UIDelay);
        DaylightSavings = (boolean)(Rotor%2);
        UpdateTimeDisplayPlus(Hour, Minute, Year, DaylightSavings);
        writeLCD(0,TopLine);
        lcd.setCursor(13,0);
        lcd.cursor();
    }

    // Set the day of the week
    Rotor = DoW + 16;   // The extra value on rotor allows easier turn-back.
    while (!ButtonPressed()) {
        delay(UIDelay);
        DoW = constrain((Rotor % 8), 1, 7);
        SetBottomLine(DoW, Month, Day);
        writeLCD(1, BottomLine);
        lcd.setCursor(0,1);
        lcd.cursor();
    }

    // Set the Month
    Rotor = Month + 26; // The extra value on rotor allows easier turn-back.
    while (!ButtonPressed()) {
        delay(UIDelay);
        Month = constrain((Rotor % 13),1,12);
        SetBottomLine(DoW, Month, Day);
        writeLCD(1, BottomLine);
        lcd.setCursor(11,1);
        lcd.cursor();
    }

    // Set the Date
    Rotor = Day;
    while (!ButtonPressed()) {
        delay(UIDelay);
        switch (Month) {
            // Thirty days hath November,
            // All the rest I can't remember.
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                Day = Rotor % 32;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                Day = Rotor % 31;
                break;
            case 2:
                Day = Rotor % 30;
                break;
        }
        if (Day==0) {
            Day++;
        }
        SetBottomLine(DoW, Month, Day);
        writeLCD(1, BottomLine);
        lcd.setCursor(14,1);
        lcd.cursor();
    }

    // Turn cursor off
    lcd.noCursor();

    // Save the new stuff, with second set to zero
    setTime(Year, Month, Day, DoW, Hour, Minute, 0x00);

    // Mark that things have changed so the next loop() does
    // what is needed, if needed.
    oldSecond = 255;
    oldMinute = 255;
    oldHour = 255;
    oldDay = 255;
}

void setTime(byte Year, byte Month, byte Day, byte DoW, byte Hour, byte Minute, byte Second) {
    //  1) Sets the date and time on the ds1307
    //  2) Starts the clock
    //  3) Sets hour mode to 24 hour clock
    //  4) Assumes you're passing in valid numbers
    Wire.beginTransmission(CLOCK_ADDRESS);
    Wire.send(0);
    Wire.send(decToBcd(Second));
    Wire.send(decToBcd(Minute));
    Wire.send(decToBcd(Hour));     
    Wire.send(decToBcd(DoW));
    Wire.send(decToBcd(Day));
    Wire.send(decToBcd(Month));
    Wire.send(decToBcd(Year));
    Wire.endTransmission();
}

void DisplayAlarmInfo(byte Alarm) {
    // This function sets the bottom line to be useful information for setting
    // the alarm(s).

    // Convert from 24-hour internal to 12-hour display
    Temp1 = (AlarmHour[Alarm]) % (byte)12;
    if (Temp1 == 0) {
        Temp1 = 12;
    }
    if (Temp1 / 10) {
        // write the 1 in this case.
        BottomLine[0] = '1';
    } else {
        BottomLine[0] = ' ';
    }
    // Second digit of hour:
    BottomLine[1] = (char)(Temp1 % 10 + 48);
    // minutes
    BottomLine[2] = ':';
    BottomLine[3] = (char)(AlarmMinute[Alarm] / 10 + 48);
    BottomLine[4] = (char)(AlarmMinute[Alarm] % 10 + 48);

    // Take care of am/pm
    if (AlarmHour[Alarm] > 11) {
        BottomLine[5] = 'p';
    } else {
        BottomLine[5] = 'a';
    }

    BottomLine[6] = ' ';

    // Is this alarm on?
    if ((AlarmOn >> Alarm) & 1) {   // Clever, here. :-( This checks the alarm bit for this alarm.
        BottomLine[7] = '!';
    } else {
        BottomLine[7] = '-';
    }

    BottomLine[8] = ' ';

    // List status for each day
    for (byte j=0;j<7;j++) {
        if ((AlarmDays[Alarm] >> j) & 1) {      // Check which days things are on using bit-shift.
            BottomLine[9+j] = OnAlarms[j];      // Capital letters for "on this day".
        } else {
            BottomLine[9+j] = OffAlarms[j]; // lower-case letters for "not on this day".
        }
    }

    // Update display
    writeLCD(1,BottomLine);
}

void SetAlarm() {
    // Picks an alarm number, sets it.

    byte ThisAlarm = 0;

    const char *M = PSTR(" Set Alarm # 0  ");
    for (byte j=0;j<MESSAGE_LENGTH-1;j++) {
        TopLine[j] = (char)pgm_read_byte(M++);
    }
   
    // First select which alarm to set
    Rotor = 3*MAX_ALARM;
    while (!ButtonPressed()) {
        ThisAlarm = Rotor % MAX_ALARM;
        TopLine[13] = (char)ThisAlarm + 49;
        // (Add 49 instead of 48 so that the alarm counts 1-7 rather than 0-6.)
        writeLCD(0,TopLine);
        // Depending on which alarm is selected, display info on second line.
        DisplayAlarmInfo(ThisAlarm);
        // Mark what is being changed
        lcd.setCursor(13,0);
        lcd.cursor();
        delay(UIDelay);
    }

    // Now an alarm number has been selected, so set it.

    // Set the hour
    Rotor = AlarmHour[ThisAlarm] + (byte)48;
    while (!ButtonPressed()) {
        AlarmHour[ThisAlarm] = Rotor % (byte)24;
        DisplayAlarmInfo(ThisAlarm);
        lcd.setCursor(1,1);
        lcd.cursor();
        delay(UIDelay);
    }

    // Set the Minute
    Rotor = AlarmMinute[ThisAlarm] + (byte)120;
    while (!ButtonPressed()) {
        AlarmMinute[ThisAlarm] = Rotor % (byte)60;
        DisplayAlarmInfo(ThisAlarm);
        lcd.setCursor(3,1);
        lcd.cursor();
        delay(UIDelay);
    }

    // Set the activity
    if ((AlarmOn >> ThisAlarm) & 1) {   // Check this alarm's on-status-bit.
        Rotor = (byte)127;
    } else {
        Rotor = (byte)128;
    }
    while (!ButtonPressed()) {
        if (Rotor % 2) {
            AlarmOn = AlarmOn | ((byte)1 << ThisAlarm); // Turn on this alarm's on-status-bit.
        } else {
            AlarmOn = AlarmOn & ((byte)255 - ((byte)1<<ThisAlarm)); // Turn off this on-status-bit.
        }
        DisplayAlarmInfo(ThisAlarm);
        lcd.setCursor(7,1);
        lcd.cursor();
        delay(UIDelay);
    }

    // Set the days it should ring
    for (byte j=0;j<MAX_ALARM;j++) {
        if (AlarmDays[ThisAlarm] & ((byte)1<<j)) {
            // On this day
            Rotor = (byte)127;
        } else {
            Rotor = (byte)128;
        }
        while (!ButtonPressed()) {
            if (Rotor % 2) {
                // Turn this day's bit on
                AlarmDays[ThisAlarm] = AlarmDays[ThisAlarm] | ((byte)1<<j);
            } else {
                // Turn this day's bit off
                AlarmDays[ThisAlarm] = AlarmDays[ThisAlarm] & ((byte)255-((byte)1<<j));
            }
            DisplayAlarmInfo(ThisAlarm);
            lcd.setCursor((9+j),1);
            lcd.cursor();
            delay(UIDelay);
        }
    }
   
    // Save alarm status to EEPROM
    SaveStatus();

    // Set oldDay to invalid value so that the bottom line gets set back to normal.
    oldDay = 255;

    // Clear the cursor
    lcd.noCursor();
}

void UpdateTimeDisplayPlus(byte Hour, byte Minute, byte Year, boolean DaylightSavings) {
    // Updates TopLine to reflect current time, with addition of
    // year and daylight savings time status.
    UpdateTimeDisplay(Hour, Minute);
    TopLine[8] = '2';
    TopLine[9] = '0';
    TopLine[10] = (char)(Year/10) + 48;
    TopLine[11] = (char)(Year%10) + 48;
    TopLine[12] = ' ';
    if (DaylightSavings) {
        TopLine[13] = 'D';
    } else {
        TopLine[13] = ' ';
    }
    TopLine[14] = 'S';
    TopLine[15] = 'T';
}

void UpdateRotation() {
    // This is the subroutine that runs every time pin 2
    // goes low.
    if (digitalRead(B)) {
        Rotor++;
    } else {
        Rotor--;
    }
}

boolean ButtonPressed() {
    // I'm using the internal pull-up resistors on the Arduino, so
    // logic is inverted. LOW means the button is pressed.
    // This routine, however, returns true if button has been pressed and false
    // otherwise. It's sensitive to the transition between unpressed and pressed.
    boolean WasIt;  // as in, "Was It pressed?"
    NewButtonState = !digitalRead(Button);
    if ((!OldButtonState) && (NewButtonState)) {
        // Button was unpressed and is now pressed
        WasIt = true;
    } else {
        WasIt = false;
    }
    OldButtonState = NewButtonState;
    return(WasIt);
}

void DealWithButton() {
    // Function to deal with button presses.
    // If the alarm is beeping it turns the beep off and returns.
    // Otherwise, it starts the whole "set time/date/alarm" routine.

    byte Next=0;    // flag to keep track of next action.
    if (AlarmRinging) {
        // Alarm is ringing, so turn it off and return.
        AlarmRinging = false;
        BuzzerState = false;
        digitalWrite(BUZZER, LOW);
    } else {
        // User must be trying to set the clock or the alarm.
        for (byte j=0;j<MESSAGE_LENGTH;j++) {
            TopLine[j] = SetMessage[j];
            BottomLine[j] = AlarmMessage[j];
        }
        Rotor=0;
        TopLine[0] = '*';
        writeLCD(0,TopLine);
        writeLCD(1,BottomLine);

        // Now wait for selection.
        unsigned long StartTimeOut = millis();
        while (!ButtonPressed()) {
            delay(UIDelay);     // user interface delay
            Rotor = Rotor % 2;

            // move selection display according to current rotor position.
            if (Rotor == 1) {
                TopLine[0] = ' ';
                BottomLine[0] = '*';
                Next = 1;
            } else {
                TopLine[0] = '*';
                BottomLine[0] = ' ';
                Next = 0;
            }
            writeLCD(0,TopLine);
            writeLCD(1,BottomLine);

            // Check for time-out
            if ((millis()-StartTimeOut) > (unsigned long)30000) {
                // No buttonpress for 30 seconds, go back to keeping time.
                Next = 2;
                // Since it skips SetClock() or SetAlarm() here, we need
                // to reset the bottom line display.
                oldDay = 255;
                break;
            }
        }
        if (Next == 0) {
            // Set clock
            SetClock();
        } else if (Next == 1) {
            // Set alarm
            SetAlarm();
        }
    }
}

void writeLCD(int lineNum, char* contents) {
    lcd.setCursor(0,lineNum);
    lcd.print(contents);
}

void Retrieve(const char *M) {
    // Retrieves a string from PROGMEM, puts it into BottomLine.
    for (byte j=0;j<MESSAGE_LENGTH-1;j++) {
        BottomLine[j] = (char)pgm_read_byte(M++);
    }
}





void SaveStatus() {
    // Saves permanent information in 1307 EEPROM.
    //  Information saved consists of the following bytes
    //  8:  Daylight Savings state
    //  9:  AlarmOn byte
    //  10-30:  sequential elements of AlarmHour array, AlarmMinute array, and AlarmDays array.
    //  The order is
    //      AlarmHour[0], AlarmMinute[0], AlarmDays[0],
    //      AlarmHour[1], AlarmMinute[1], AlarmDays[1],
    //  etc.

    Wire.beginTransmission(CLOCK_ADDRESS);

    // Start with memory location 8 (the first 7 are time).
    Wire.send(0x08);

    // Write the Daylight Savings flag.
    Wire.send((byte)DaylightSavings);
   
    // Write the AlarmOn byte.
    Wire.send(AlarmOn);

    // Write the array data.
    for (byte j=0;j<MAX_ALARM;j++) {
        Wire.send(AlarmHour[j]);
        Wire.send(AlarmMinute[j]);
        Wire.send(AlarmDays[j]);
    }

    // And done...
    Wire.endTransmission();
}

void LoadStatus() {
    // Recalls data from 1307 EEPROM on powerup.
    // Start by pointing to the data we want
    Wire.beginTransmission(CLOCK_ADDRESS);
    Wire.send(0x08);
    Wire.endTransmission();

    // now get data bytes
    Wire.requestFrom(CLOCK_ADDRESS, 2+(MAX_ALARM*3));
    DaylightSavings     = Wire.receive();
    AlarmOn             = Wire.receive();
    for (byte j=0;j<MAX_ALARM;j++) {
        AlarmHour[j]    = Wire.receive();
        AlarmMinute[j]  = Wire.receive();
        AlarmDays[j]    = Wire.receive();
    }
}

byte decToBcd(byte val) {
    // Convert normal decimal numbers to binary coded decimal
    return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val) {
    // Convert binary coded decimal to normal decimal numbers
    return ( (val/16*10) + (val%16) );
}

void getTime(byte *Year, byte *Month, byte *Day, byte *DoW, byte *Hour, byte *Minute, byte *Second) {
    // Gets the date and time from the ds1307
    // Reset the register pointer
    Wire.beginTransmission(CLOCK_ADDRESS);
    Wire.send(0);
    Wire.endTransmission();

    Wire.requestFrom(CLOCK_ADDRESS, 7);

    // A few of these need masks because certain bits are control bits
    *Second = bcdToDec(Wire.receive() & 0x7f);  // second
    *Minute = bcdToDec(Wire.receive());         // minute
    *Hour   = bcdToDec(Wire.receive() & 0x3f);  // hour, change for 12 hour am/pm
    *DoW    = bcdToDec(Wire.receive());         // Day of week
    *Day    = bcdToDec(Wire.receive());         // Day of month
    *Month  = bcdToDec(Wire.receive());         // month
    *Year   = bcdToDec(Wire.receive());         // year
}

void UpdateTimeDisplay(byte Hour, byte Minute) {
    // Updates TopLine, characters 0-10, with time and alarm info.
    Temp1 = Hour % 12;  // Convert 24h clock to 12h
    if (Temp1==0) {     // 00 is actually 12 on am/pm clock.
        Temp1 = 12;
    }
    if (Temp1 / 10) {
        // write the 1 in this case.
        TopLine[0] = '1';
    } else {
        TopLine[0] = ' ';
    }
    // Second digit of hour:
    TopLine[1] = (char)(Temp1 % 10 + 48);
    // minutes
    TopLine[2] = ':';
    TopLine[3] = (char)(Minute / 10 + 48);
    TopLine[4] = (char)(Minute % 10 + 48);
    // Take care of am/pm
    if (Hour > 11) {
        TopLine[5] = 'p';
    } else {
        TopLine[5] = 'a';
    }
    TopLine[6] = 'm';
    TopLine[7] = ' ';
    // Alarm indicator at [10] and [11]
    if (AlarmOn) {
        TopLine[8] = 'o';
        TopLine[9] = 'n';
    } else {
        TopLine[8] = '-';
        TopLine[9] = '-';
    }
    TopLine[10] = ' ';
}

void SetBottomLine(byte DoW, byte Month, byte Day) {
    // Loads the day/date information into the second line of the display.
    for (byte j=0;j<MESSAGE_LENGTH;j++) {
        BottomLine[j] = WeekDay[DoW-1][j];
    }
    // Update date display
    if (Month<10) {
        BottomLine[11] = ' ';
    } else {
        BottomLine[11] = '1';
    }
    BottomLine[12] = (char)(Month%10)+48;
    BottomLine[13] = '/';
    if (Day<10) {
        BottomLine[14] = (char)Day+48;
        BottomLine[15] = ' ';
    } else {
        BottomLine[14] = (char)(Day/10)+48;
        BottomLine[15] = (char)(Day%10)+48;
    }
}



void setup() {

    // Configure the switch, button, and buzzer.
    pinMode(Button, INPUT);
    pinMode(A, INPUT);
    pinMode(B, INPUT);
    pinMode(BUZZER, OUTPUT);

    // Start the I2C interface to the 1307 clock
    Wire.begin();

    // Start the LCD
    lcd.begin(2,16);
    lcd.clear();

    // Attach interrupt to pin A of the rotary switch
    attachInterrupt(0, UpdateRotation, FALLING);

    // Get things from the eeprom
    LoadStatus();

    // Set old information to absurd values to trigger new second/minute/hour/day actions

    oldHour = 255;      // These three are set to absurd values to trigger
    oldMinute = 255;    // the change-sensitive bits in loop().
    oldSecond = 255;    //
    oldDay = 255;       //
}

void loop() {
    /*  In this main loop, several things should happen.
       
        Every quarter second:
            Check the time.
            Check to see if the button is pressed.
                If so, handle it and come back when finished.
            If an alarm is ringing, toggle the buzzer on/off.
       
        Every second:
            Update the time display.
            Update temperature display.
            If it's an even second, swap message/date info on line 2.

        Every minute:
            Check to see if an alarm should be ringing.
                Turn it on or off as necessary.
           
        Every hour:
            Check for daylight savings time shifts and other specials.
            Adjust backlight for daylight/evening conditions.

        Every day:
            Check for a message to put on line 2.
            Update the display of line 2
    */

    // These are the things that happen every cycle.
    getTime(&Year, &Month, &Day, &DoW, &Hour, &Minute, &Second);

    if (ButtonPressed()) {
        DealWithButton();
    }

    if (AlarmRinging) {
        // Toggle buzzer on/off
        BuzzerState = !BuzzerState;
        digitalWrite(BUZZER, BuzzerState);
    }

    // These are the things that happen every second:
    if (Second != oldSecond) {
        oldSecond = Second;

        UpdateTimeDisplay(Hour, Minute);
      
        // Write to the LCD
        writeLCD(0, TopLine);
        if (Second%4<2) {
            // Display this for 2 seconds
            writeLCD(1, Message);
        } else {
            // Display that for 2 seconds
            writeLCD(1, BottomLine);
        }
    }

    // These are the things that happen every minute:
    if (Minute != oldMinute) {
        oldMinute = Minute;

        // Deal with alarms
        // Assume that nothing is happening.
        AlarmRinging = false;

        // Now check to see if anything IS happening.
        if (AlarmOn) { 
            // Loop through the set of alarms, check which is on
            for (byte j=0;j<MAX_ALARM;j++) {
                if ((AlarmOn >> j) & 1) {   // Check each AlarmOn bit
                    // alarm j is on, check time for alarm j.
                    if ((AlarmHour[j]==Hour) && (AlarmMinute[j]==Minute)) {
                        // Check day for alarm j
                        if ((AlarmDays[j]>>(Day-1)) & 1) {
                            // Alarm j should be ringing.
                            AlarmRinging = true;
                        }
                    }
                }
            }
        }
    }

    // These are the hourly events:
    if (Hour != oldHour) {
        oldHour = Hour;

       

        // Check for time changes every Sunday
        if (DoW==1) {
            // It's Sunday. Check the rest of the requirements
            if ((Month==3) && (Day>7) && (Day<15) && (!DaylightSavings)) {
                // This is the day to "Spring Forward"
                if ((Hour==2) && (Minute==0) && (Second==0)) {
                    // set reminder
                    Retrieve(PSTR("Set clocks ++   "));
                    // Set hour up by one.
                    Hour++;
                    setTime(Year, Month, Day, DoW, Hour, Minute, Second);
                    // Set DaylightSavings flag
                    DaylightSavings = true;
                    // Save DS flag to EEPROM
                    SaveStatus();
                }
            }
            if ((Month==11) && (Day<8) && (DaylightSavings)) {
                // This is the day to "Fall Back", and we haven't fallen back yet.
                if ((Hour==2) && (Minute==0) && (Second==0)) {
                    // Remind people
                    Retrieve(PSTR("Set clocks back "));
                    // Decrement hour
                    Hour--;
                    setTime(Year, Month, Day, DoW, Hour, Minute, Second);
                    // Clear DS flag
                    DaylightSavings = false;
                    // Save DS flag to EEPROM
                    SaveStatus();
                }
            }
        }
    }

    // These are the daily events:
    if (Day != oldDay) {
        oldDay = Day;
   
        SetBottomLine(DoW, Month, Day);
        // Update the day's special message

    }

    // Wait around a bit before repeating.
    delay(250);
}
 

http://hacks.ayars.org/2010/01/alarm-clock-overkill.html

Nessun commento:

Posta un commento