=============================================================================================================

Tuesday, May 10, 2011

microSD ATmega32 Data-Logger



Hi friends,

aim of this project is to present a way to store a large quantity of data into microSD card in files with FAT32 format. Here, ATmega32 is used for data collection and microSD interface. The data is received from in-build 8-channel ADC of ATmega32. One channel is used for reading temperature from LM35 sensor and remaining channels are used for simply reading voltages and storing them.

This project can be used to interface 8 different sensors with ADC of ATmega32, similar to the LM35 used here. The data is stored in CSV (comma separated values) format, which can be read using a PC/Laptop with Microsoft Excel or other compatible software. A snapshot of the excel file is given later in this post.

This project is an example of how to use the microSD FAT32 library presented in my earlier post. In that post, the files were created using hyper-terminal and entering data with the PC keyboard, since that demonstrates the file creation and it's easy to debug. But many users have requested to make the file creation independent of the terminal, done inside the microcontroller, so I'm showing here how to use those functions independent of terminal. If you have directly landed on this page, it would be more helpful if you visit the original post first as it would be a better starting place for learning SD or FAT32 functions.

Here is the schematic (click on the images for larger view or download PDF):

The project contains RTC interface (for date and time storage), RS232 (for connection with PC) and a microSD module. Here, the hyper-terminal connection is required only for setting RTC date and time. Once the date/time are set, the RS232 connection is not required anymore for normal data-logging operation (It can be used for debugging purpose if there is a problem).

The microSD module used here is from eXtreme Electronics.
The module is shown in the figure here. Other than the microSD socket, this low-cost module also contains on-board 3.3v regulator for the microSD card, a 5v-3.3v level converter and other safety features required for the card. This module is used here as it provides a stable interface and makes the the card compatible with 5v supply and 5v signals of microcontroller.

The module is available at: http://store.extremeelectronics.co.in/MicroSD-TF-Module.html

The schematic also shows two LEDs and a push-button. The LEDs are used for indications of power and recording and the push-button is used to start-stop recording.

Operation of the circuit:
-----------------------------------------------
For setting RTC date/time (or for debugging mode):
  • Connect the microSD module, insert the microSD card
  • Connect the RS232 cable with the circuit. Set-up hyper terminal with 19200 baud, no parity, 8-bit data, 1 stop-bit and flow-control as 'None'
  • Connect the power cable and power on the circuit while keeping the push-button pressed
  • Green LED will glow in the circuit board
  • A menu will be displayed on the Hyper terminal as shown in the figure below. Select desired option and follow the displayed instructions
  • When date/time is set or debugging done, select option '0' to come out of the menu and start functioning a s data-logger
  • At this point, the RS232 cable can be removed


Operation as Data-Logger:
  • Connect the power cable and power on the circuit
  • Green LED will glow
  • Whenever the data-logging is required, press the push-button
  • Red LED will glow, indicating that the recording has started
  • To stop recording, press the push-button again, recording will stop and red LED will turn off
  • Files stored in the card can be read using a PC card-reader or using hyper-terminal with the circuit started in debugging mode

The operation is very simple as it uses just one push-button and an LED indication. In case of any error in accessing the card, red LED will blink continuously. In such a case, you can start circuit in debug mode (with terminal) and see the error messages.

Files are stored with the date as a name and .CSV extension. For example, data-logging done on 10 May 2011 would be stored in "10052011.CSV" file. Since the date is the name of file, everyday a single file is created and all the data recording done in a day goes into single file, no matter how many times the recording is stopped/started. First column of the file shows date, second shows time and next 8 columns show data from the 8 channels.

A file created during testing is shown in the figure below, where 5 sec interval was set for measurements (click on the image to enlarge it). Here channel-0 was used for LM35 temperature sensor, and remaining channels measure voltage. 5v was connected to channel-1 and 3v Li cell was connected to channel-3 (Channel 2 & 4 show some small voltages due to noise from voltages connected to nearby channels, which can be corrected by using bypass caps).


The interval between two measurement cycles is defined in main.c file, which can be set as per the user requirement. Basically, the program forms a dataString in every measurement cycle and appends this string to the file, if the file already exists or it creates a new file (for example, during the first recording in a day). You may go through the comments in the source code file for more info.

Note: Make sure that RTC circuit is properly connected, otherwise the code will simply hang waiting for receiving date & time from RTC

Download project files
--------------------------------------
The source code is written in AVR-GCC format using winAVR with AVRStudio-4, complete AVRStudio project folder can be downloaded from here:

- Download Source Code

Download schematic:
- Schematic (PDF)
- Schematic (EAGLE)

References:
--------------
Visit my earlier post for the references on SD card & FAT32 which are given at the end of the post

168 comments:

Anonymous said...

Great job Sir!

James said...

Gr8!! I have already tested your earlier project on SD cards and FAT32 without any problem. I'm glad u finally posted this one which doesn't need the terminal to create files. Thanks for sharing!

Nagaraj.P.R said...

Great job!!!

Matej said...

Excellent! I think this will be easily portable to other AVRs, like ATmega128.
Are the other files the same as in the earlier project? For example I2C? I have added other I2C peripherals and would like to reatin their functionality while implementing the datalogging features to the device.
Perhaps a little "feature request": Would it be possible to keep datalogging running while temporarily ejecting the card (for the purposes to download the data to PC)? The data would be temporarily going to AVR internal EEPROM (or maybe even flash?) and after inserting the card back in, the data would be flushed. This way, no data would be lost in the process .. ?

CC Dharmani said...

Hi Matej,

the remaining I2C functions are same as the earlier project.

The EEPROM part will ask for addition of little bit of hardware and some more software functions, which may not be required in case of most users. But yes, in case of non-stop data recording, even when the card is ejected, EEPROM part would be necessary. May be in the future I can provide this customization as a separate download, but till then, you can also give it a try to do the modifications.

santoshaxl said...

how to set time and date before starting system ?

Venice said...

Great job, and thanks. I am trying to configure this circuit as well.
By the way, is it 8 Mhz-internal used or is it external 16MHz crystal?
because i noticed that the schematic diagram written 16MHz external crystal, but the coding .c file mentioned 8MHz-internal. So which one is correct? I programmed the code into Atmega32 but cant see anything on the hyper terminal. If it is 8MHz-internal, how to configure it?

CC Dharmani said...

@Santosh: During the first start-up, as mentioned in the operation of the circuit, connect the RS232 cable, open hyper terminal (19200 baud, no parity, flow control-none) and power on the circuit while keeping the pushbutton pressed. You'll see the options on the terminal for setting date/time etc.

@Venice: Yes, the controller is working at 8MHz internal. The external crystal is optional. For setting the internal clock, you need to change the fuse values. You can use following values:
Low fuse: 0xE4
High fuse: 0xD9

For more details of fuse values, refer to 'memory programming' section of the avr datasheet.

Venice said...

I re-checked and yes, I have previously set the fuse bit correctly. However, I still cant see anything in hyper terminal although I pressed the button once the circuit is powered. Baud rate 19200 and COM port are all correct. Well, I will continue to try and to figure out which part is wrong.
by the way,
1) the button is active low right?
2) in the schematic diagram, the microSD module, pin 6 connecting to PB3, from the extremeelectronics module, I saw pin 6 is CD. What is this pin? Since on MicroSD, there are only pins such as GND, VDD, CS, MOSI, MISO, SCK. So where comes the CD in normal MicroSD adapter.
Please advice and thank you.

CC Dharmani said...

Hi Venice,
if you want to see the full menu, you need to keep the button pressed while powering on, that means you press the button first and keep it pressed when you power the circuit on. But anyways, even if you don't press the button, you'd atleast get the first three lines on the terminal, mentioning the project title. May be you can recheck the RS232 connections, because the terminal would show atleast some garbage, if only the baudrate is not matching.

Yes, the button is active low.

The CD pin is actually 'Card Detect' pin which is a switch mechanically connected with card insert mechanism. I've shown it in the schematic, but not used it in the program, just to make the code compatible with previous versions. But it can be used to detect whether the card is really inserted or not.

paresh said...

hello sir, it's a great post but my question is do we need to attach external module for sd card connection compulsary. and if so why to use that one.

Anonymous said...

can we read the data again? for example I want to log data first. after complete, i read the file again and sent the data to computer using serial

CC Dharmani said...

@Paresh:
No, it's not compulsory to use external module for the microSD card, it's used here to make job simpler. But if you don't have access to such a module, you can use method suggested in schematics of my previous post:
http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

@Anonymous:
Yes, you can. Once the data is written and file is created or updated, you can stop the logging and go to debug mode as mentioned in the operational procedure, to read the file using hyper-terminal.

Walther Grube said...

Great job! I'm currently building a similar project, using an Arduino nano. I'm using FAT16lib, works fine, but I will try FAT32 eventually. I'm also using LM35 sensors, combined with a RC damper (as in the datasheet), this gives better results. I'm waiting for some sensors (CO2, humidity, pressure, etc) that I've bought to complete my circuit.

Venice said...

Hi, I have managed to log the values into SD card.
However, for the 3v lithium battery, should the '-' of the 3v batt common ground with the entire circuit?
Because if I common ground the 3v batt with the entire circuit, the LED indicating error kept blinking once I press the button to start the data log. If without the 3v Li Batt, the data can be logged, but the time cannot keep counting once the circuit is power off. So what's the problem that causing the LED to keep blinking everytime when I common ground the '-' of 3v natt?

CC Dharmani said...

@Venice:
The battery -ve terminal has to be connected with common GND of the circuit. It's recommended in the datasheet of the chip, too. The problem you are facing must be related to some other connections, as I've used the RTC chip in this way in many of my circuits and I do not find such problem. You can check out if the +ve terminal of battery is touching to other lines. Do write back here if problem still persists, I will try to create it in my circuit to find how it happens.

biswajit said...

sir myself biswajit from bbsr orissa. i am a big fan of your blog. i have tried all your posts and it works fine. but i have a doubt regarding that cluster, sector and block in microsd. i will be thankful if you help me out. thanks in advanc

Anonymous said...

I have tried to run this code in avrstudio 4 but at the time of 'build' operation it gives 43 numbers of error. (e.g- undefined reference to 'spi_init', undefined reference to twi_init' etc. Could anyone help me

Xavi said...

Great post man, keep up the good work!! Very useful ;)

CC Dharmani said...

@Anonymous:
Hey man, lemme know what's wrong!! The only reason I find about the errors is that u might be using some other compiler!! Use winAVR with AVRStudio and you won't have fortune to see a single error as compared to your 43 errors!!!

@Xavi:
Thanks, buddy!!

Anonymous said...

I want to measure the time difference between two events (using different ports) in nano second range using atmega 32 microcontroller. I will be highly obliged if anyone help me.

aslan said...

Great Job sir!

amiR said...

hello..
great post.
may i ask a stupid question because i am getting started with building a uC project.
can i start recording without press the button first??
i am building a security system.
i want to start recording all events captured by lm35 and magnetic switch after the system being activated and automatically stop recording after deactivate it.

CC Dharmani said...

@amiR:

Certainly u can do that.. U need not press the button at all, just put up the condition which makes the system to activate and deactivate, in place of pressing the button, you'll get the recording..! You need not press the button at all..

If you still have some confusion, send me your schematics and mention which input you are using for button input, I'll send u the modified code..

CC Dharmani said...

Thank you, aslan!!

amiR said...

actually i built my first project using codeVision AVR compiler.
then i found your post which is using winAVR.now i'm trying to understand your code.
honestly i confuse whether i modify your code or keep using my code but with extra time for understanding your code and applying in my code.

i will send my last schematic.
sorry because i'm very very newbie.
i'm very grateful to you for your help.

amiR said...

i've sent my last schematic to your email.

i wish you could help me,thank you so much..

Anonymous said...

do you have a sd card version?
thanx

Madhu said...

Dear sir,

I used your schematic of ATmega8 interfaced with SD card and the code that you have provided. I tried to fill the memory of 1GB card. I am able to write 128 text files only using FAT 32. If i write more files they are getting corrupted. I am able to write only 32 MB of 1GB card. Please suggest me how to access whole of 1 GB memory or how to write more than 128 text file into sd card?

Expecting for you reply,

Thanks and regards,

Madhusudan

haziqchi said...

hello sir...i like your idea of this project...and i intend to do this project for my final year project and i would like to combined it with this project (http://embedded-lab.com/blog/?p=2425) can you list all the components needed to build this data logger project....really appreciated if you can reply asap....here are my email peace_world91@yahoo.com

lee english said...

hello sir..can i know where i can get this project??and how much cost of it??how many days for shipping to malaysia?? please email me at hazel_must@yahoo.com..thanks..

Luca said...

nice job and namastē for having shared it. Just a curiosity of mine: what is the maximum sample rate? I mean, according to the source it can goes up to 100 Hz (100 S/s) but the rtd you used has 1 s of resolution.
BR
Luca

CC Dharmani said...

@Luca: Ya, though I've mentioned the max. sampling of 100Hz (min. delay of 10ms), due to RTC's resolution limit, time will be written same for those readings taken during the same second.
(Note: The actual max. rat will be less than 100Hz, since the delay loop written there doesn't account for overheads occurring in calling functions, writing to the card etc.)

james said...

hye sir!!i'm glad 2 see your work!!can u help me please??can RPM replace the temperature sensor and what is RPM ic??can it be combine together with your circuit sir??

Shagufta said...

Hai...
Great work sir. Iam doing a project to send 32 bit data from atmega32 to pc using RS232 serial communication. I know to transfer 8 bit data but I dont know how to transfer 32 bit data. Please help me.

Regards,
Shahgufta

Mika said...

Hi,
I,ve downloaded your code, put it to my new atxmega256a3b and nothing worked...Initialization passed(returned 0, card type-standart capacity), but when I'm trying to get boot sector data, function returns 1...I was debugging code for whole day, piece by piece and find out, that uC doesn't get start block token (0xfe). I've formatted my SD card (2GB) as FAT32, with 512 b. MAby something wrong with initialization, that card doesn;t understand next commands or card formatted wrong...
Please help :)

Mika said...
This comment has been removed by the author.
Mika said...

Hi again,
I've solved problem, now it's working :) My avr studio 5 Gcc compiler doesn't change types of variables, no typecasting.

kiptoo said...

hi sir been trying to build and compile the source code given in thi post version 1.0 but it has so many undeclared functions.
your assistance on this will highly be appreciated as it will help me in debugging of codes

kiptoo said...

I have tried to run this code in avrstudio 4 with AVRGCC compiler but at the time of 'build' operation it gives 43 numbers of error. (e.g- undefined reference to 'spi_init', undefined reference to twi_init' etc.i believe the winAVR you were refering to is the AVRgcc

Anonymous said...

Good Job, great work.
However, I found there's a bug. Whenever the appended filesize equals to the multiples of the cluster size (512bytes), forexample, when I write 128 times of 20 bytes data, = 2560bytes. Starting from 2560bytes, the file will be corrupted and can't be recognised by Windows.

Mika said...

yes, there are bugs with corrupted files...my uC restarts, because of SD card writing failure and trying to write to the same file, the file is corrupted and uC restarts over and over again...could you tell me where is mistake in code?thank you

Mika said...

Hi again,

In my opinion(it may be wrong), there is something wrong with function - writeFile. When function appending an existing file i overflows 512 -max value:

i = fileSize % bytesPerSector;
while((data != '\n') && (k < MAX_STRING_SIZE)) {
data = dataString[k++];
buffer[i++] = data;
}

bytesPerSector=512, so if fileSize is not mulptiple by 512, i overflows...

Maby something is wrong in my thoughts, please comment.
Thank you.

Mika said...

I'm very sorry about last comments, I'm newby in programming and electronics... i was looking for mistakes in code and analyzed doubtfull parts:) it is everything OK with code, my Xmega permanently restarted due to my fault, not because of the code... my hardware was bad -I forgot to put schottky to my switch mode power supply, so when uc was starting writing to SD card, voltage dropped down and BOD protection restarted uC...

rinki arora said...

can u please tell me how to attach file in any blog for new post. i have just now create my eblog nw i have copy my word document in one of the post but images r not copied to it....and i don't want to insert single image. i ust want to attached some file which i have made...so plz help me..i also want to create blogs.
my email id is rinki.arora88@gmail.com

Ken said...

Hello Sir,
nice job here. Please I want to find out if I can use this data logger to measure and store the voltage and current from a 12V car battery. Thanks.

kajianmaskam said...

thats awesome sir...
but i totally dont understand :), because maybe i havent learn avrgcc yet, i usual use codevision for the compiler, and the sintax little bit different between them...
would you write it in codevision?
thanks a lot.. :)

kajianmaskam said...

especially, when write data with elm chan lib.
like in the example:
char text[]="i like codevision avr";

to logging the data like:
char text[]=" %d , %d \r\n", temp, volt; <<-- but it doesnt work..

Mohan Meena said...

Hi
I was going though your code which supports SDHC card. The routine which converts normal short file name into FAT format. I could not get this part of the code which is used for setting file extension. Can you please explain the concept used in this. For setting file extension we use first three letters of file extension. Are there any other rules also for that? What if file have only 2 character extension?
Please reply soon


for(k=8; k<11; k++) //setting file extention
{
if(fileName[j] != 0)
fileNameFAT[k] = fileName[j++];
else //filling extension trail with blanks
while(k<11)
fileNameFAT[k++] = ' ';
}

sidd said...

sir we need a code to display content of text file stored in mmc on 16x2 lcd

mst said...

Hi, how can I faster sampled data. I need about 100-200 Hz. I'm not using RTC, I tried different settings in ADX, but it does not change anything. I removed the loop with the INTERVAL. Please help me.

mst said...

Not in the ADX but the ADC ;)

Vicky V.K. said...

Respected Sir,
Its great project, I am final year student, i choose your project for my final year project but i want to add another four different sensors for remaining port pins in portA so which kind of changes required in schematic as well as in your posted coding, i was tried to add by taking yours posted coding reference but bugs are in my coding still remains. I really want your valuable Guidance Sir.
Thank you

Yours Student
Vicky V.K.

Vicky V.K. said...

If you suggest me the changes in coding and schematic please send on my mail id: vkcontrol@rediffmail.com

sunil said...

Hi sir,
Gr8 job.I am using silabs mcu i.e c8051f930 using this with SPI i need to write and read the data from micro sd please suggest whether your code is working with this fine or not?.Please let me know.
Thanks.

Julchen said...

After reading this, I've decided NOT to buy microSD cards from China. Ever. I'll stick to official trusted retailers only. I'm sure SD cards and FAT32 will really work flawlessly for me too.

ivante said...

Just drop by to say thanks a lot for the share!
I had a datalogger project which log current, voltage, and temperature data for recording photovoltaic activities. At first I kinda confused but thanks again it's now Done!

Regards,
ivantriyadi@gmail.com

nghia said...

Hi you, I have an electronic scales the second line RXD and GND pins, please help me save the signal circuit on sd card, always ask for their code and how to load code, thank you very much

nghia said...

Hi you, I have an electronic scales the second line RXD and GND pins, please help me save the signal circuit on sd card, always ask for their code and how to load code, thank you very much

Pay said...

Hey, I need to sample datas at around 200Hz. with only a start and stop time stamp. Could any body able to solve this contact hyy_shbz@hotmail.com.
with 100 pounds reward.

Thank you.

Arup said...

Idea: Taking 200 Samples per second from ADC is normal. But the SD writing requires more time. You need a large RAM to store the intermediate results. And write to SD card when stop button is pressed, with the timestamp.

Raphael said...

Hello

Very helpful project!
If I am building the project (Eclipse, AVR Plugin, GCC) I get a flash size of 23.8kByte and 1kByte for RAM. Is that possible or do I something wrong?
I just switched from IAR to Eclipse AVR GCC.

Vladimir said...

Great work like allways from you Sir!

I understand your rutines works perfectly to others, however my files get corrupted if they grow bigger than 68kB(allways). I'm debbuging this very long, tried different SD-s, frequencies, configurations, even without clock chip. Using your original rutines.Do you have some possible clues for this?

Anonymous said...

I have one question how to change calc dor ADC in ADC_routines.c to show real voltage ?

I whant to measure voltages in range 0...20V
ADC measure 0...5V

With ADC divider when show 5V its 20V actualy, 2,50V its 10V, 4,00V its 16V,...
Calc should be : max real voltage (20V) / max ADC (5V) = 4 4 x ADC value = real voltage value


How to add this calc to C source for this ?

I whant that real value to have on SD, after importing in Excel I have real data.

Anonymous said...

i am using ATmega32A. i compile this source code using AVR Studio 4.

and i got 41kB hex file. and it doesnt fit into my atmega32 which only has 32kB of ROM..

is there anybody has this kind of problem?

thank you

Anonymous said...

I think this blog is dead noone answer.

CC Dharmani said...

@Anonymous:

The Hex file size doesn't matter. What matters is the code size displayed when you compile the code. That will be around 12-13 KBytes. So, don't worry about the 41k size of hex file itself.

(When you dump the code into microcontroller, the programmer doesn't dump the hex file itself into the controller, but first the hex file is decoded and only the code bytes are sent to the controller).

Anonymous said...

Anonimuous did you solve that recalc I need that also, to measure up to 20-30V?

Anonymous said...

I found there calculations but dont know it is right for this:
http://www.circuitvalley.com/2012/02/30-volts-panel-volt-meter-pic.html

CC Dharmani said...

Hi,
the link which you provided describes the usage of resistor divider method to measure voltages greater than 5v. You can also follow the same way.
In C code, you'll just need to multiply the value read by ADC with your resistor divider constant. e.g. if you want to measure 20v, you need voltage divider of 4 atleast, and hence, you can multiply the ADC value in the code with 4 to display actual value.

Anonymous said...

Am I make it right?

In file ADC_routines.C this changes to measure 20V :

volt = (float)(value * 5.0)/ 1024.0;
value = ((unsigned int)(volt * 1000))*4;

Jigs PATEL said...

hello sir......
i want to interface at89s52 and micro sd card so please give me a coding and cuircuit diagram for that........
thank you.........

CC Dharmani said...

@Anonymous:

Do it this way for proper display of string:

volt = (float)(value * 5.0)/ 1024.0;
value = (unsigned int)(volt * 100)*4;

voltage[6] = 'V'; //V for voltage
voltage[5] = ' ';
voltage[4] = (value % 10) | 0x30;
value = value / 10;
voltage[3] = (value % 10) | 0x30;
voltage[2] = '.';
value = value / 10;
voltage[1] = (value % 10) | 0x30;
value = value / 10;
voltage[0] = value | 0x30;

This will display two digits before decimal point and two digits after decimal point ("xx.xx V" format)

Anonymous said...

Big Thanks for answer,
I'm just curious and whant to learn if I whant to show up to 100V (format XXX.X V) it will be like this?


volt = (float)(value * 5.0)/ 1024.0;
value = (unsigned int)(volt * 100)*19,8;

voltage[6] = 'V'; //V for voltage
voltage[5] = ' ';
voltage[4] = (value % 10) | 0x30;
value = value / 10;
voltage[3] = '.';
voltage[2] = (value % 10) | 0x30;
value = value / 10;
voltage[1] = (value % 10) | 0x30;
value = value / 10;
voltage[0] = value | 0x30;

Anonymous said...

I make mistake I put 19,8 instead 20.

Anonymous said...

for 100V should be like this ?

volt = (float)(value * 5.0)/ 1024.0;
value = (unsigned int)(volt * 10)*20;

voltage[6] = 'V'; //V for voltage
voltage[5] = ' ';
voltage[4] = (value % 10) | 0x30;
value = value / 10;
voltage[3] = '.';
voltage[2] = (value % 10) | 0x30;
value = value / 10;
voltage[1] = (value % 10) | 0x30;
value = value / 10;
voltage[0] = value | 0x30;

Anonymous said...

Can SD card be connected like this :
http://3.bp.blogspot.com/_zqABT3suzXE/S_EwMNYzDhI/AAAAAAAAAW4/MHHY0U-xAhM/s1600/SD_M32_RTC.JPG

Instead of using sd/mmc adapter card reader?

Anonymous said...

Is it possible to make logger channels like this :

Date

Time

2 Temp (In & Out)

3 Voltage 0-20V for voltage

3 Voltage 0-5V for amperage (up to 5V for current shunt)

CC Dharmani said...

@Anonymous:

the code for 100v is ok.

yes, you can connect the card as per that schematic.

you can have the data-logging in the format you mentioned.

Anonymous said...

Pins connection with SD card are :

ATMega32 pin 8 to SD card SCK,
ATMega32 pin 7 to SD card MISO (DO),
ATMega32 pin 6 to SD card MOSI (DI),
ATMega32 pin 5 to SD card SS (chip select CS),
ATMega32 pin 4 to SD card ???

I dont know where go ATMega32 pin 4.

kajianmaskam said...

/

Anonymous said...

I make some changes for reading ADC channels:
1 chanel temp
3 channels 0-5V
4 channels 0-30V



I make some changes in main.c ADC_routines.c ADC_routines.h files.


ADC_routines.h

I add just this line :

void readVoltage_b(unsigned char);


ADC_routines.c

In this file I add this :

void readVoltage_b(unsigned char channel)
{
unsigned int value;
float volt;

ADMUX = 0x40 | channel;
value = ADC_read();

volt = (float)(value * 5.0)/ 1024.0;
value = (unsigned int)(volt * 100)*6;

voltage[6] = 'V'; //V for voltage
voltage[5] = ' ';
voltage[4] = (value % 10) | 0x30;
value = value / 10;
voltage[3] = (value % 10) | 0x30;
voltage[2] = '.';
value = value / 10;
voltage[1] = (value % 10) | 0x30;
value = value / 10;
voltage[0] = value | 0x30;
}



main.c

In this file I add and change this (I marked red) :

for(i=0; i<10; i++) dataString[i] = date[i];
dataString[i++] = ',';

for(j=0;j<8; j++) dataString[i++] = time[j];
dataString[i++] = ',';

readTemperature(0); //read temperature from adc channel-0
for(j=0;j<7; j++) dataString[i++] = temperature[j];


for(channel=1; channel<4; channel++) //read voltages from ADC channel 1 to 3
{
dataString[i++] = ',';
readVoltage(channel);
for(j=0;j<7; j++) dataString[i++] = voltage[j];
}

for(channel=4; channel<8; channel++) //read voltages from ADC channel 4 to 7
{
dataString[i++] = ',';
readVoltage_b(channel);
for(j=0;j<7; j++) dataString[i++] = voltage[j];
}

dataString[i++] = '\r';
dataString[i] = '\n'; //always end the string with these two characters,
//before calling the writeFile function





Can You verify that code is OK ?

I didnt built this project for now, but I awaiting ATMega32 in TQFP44, I dont have where to try to see if it works or not sorry.

Anonymous said...

no answer?

CC Dharmani said...

Ya, the code if fine. You can go ahead with the circuit implementation.

Anonymous said...

I'm very grateful to You, You help me lots, specialy in learning of code. Big Thanks again.

tewodros said...

hi great worke please post the proteus simulation for this fat32 format. i checked with hardware setup and it works fine but when i simulate with proteus i fails please upload it with .mmc file also
tank's

SergeyA said...

I found next issue. Code removes at least one cluster from free memory record even if you append one byte to the free space of last sector of existing file.
Do I do something wrong or you have fix for this bug?

Sumanta said...

Myself Sumanta is facing a problem with storing data in microsd card through the adapter. I have not used Microsd module connector but has fixed the microsd adapter in the bredboard with wires soldered on it. I have done all the connections as they are in the circuit diagram of the datasheet. But after powering on the red LED is blinking and nothing is displayed into Hyperterminal. Can anyone please help me to sort out the problem.

Sumanta

Dhairya Ganatra said...

Hi!
Awesome blog...
I recently worked on atmega32 project,I facing some interfacing problems in my project can u plz help me it's urgent.this is my final year project and i try my best to do it but I can't solve it,so plz help me..

My project is "Monitor and control of green house environment"(using atmega32)

Plz Reply me on:

theunknownever@gmail.com
dhairyaganatra@gmail.com

Dhairya Ganatra said...

Hi!
Awesome blog...
I recently worked on atmega32 project,I facing some interfacing problems in my project can u plz help me it's urgent.this is my final year project and i try my best to do it but I can't solve it,so plz help me..

My project is "Monitor and control of green house environment"(using atmega32)

Plz Reply me on:

theunknownever@gmail.com
dhairyaganatra@gmail.com

Sumanta said...

I am not using the MICROSD module and microsd module connector but instead of that I am using Microsd adapter with memory card. I have
soldered pins on microsd adapter and have fixed it on the Bredboard. Then I have wired it with ATMEGA32 according as shown in the circuit diagram of the datalogger. Will in that way the memory card cannot be accessed by the microcontroller. Please have your suggestions.


Sumanta

natalie said...

wow perfect sharing for my problem. I gotta see other related discuss to get it more. thanks also to the people who drop and share their expertise.
printing hervey bay

Majid said...

Hi
first excuse me because my english grammar is not good enough.
I see your nice project on http://www.dharmanitech.com/2011/05/microsd-atmega32-datalogger.html and now I'm very glad too find you.
I need a project like this for my school with avr codevision and 2 diferrences :

1 - I want to use an srf02 sonar module and log the distance + 2 ADC ports for voltage logging.

2 - I want the datasheet of microSD module to make the that in my home for my project.because in my country this module was not available.or if there is a way to connect the micro SD or SD directly to atmega32 can you tell me that ?

it's urgent .
thanks

Majid said...

I need help on this project.why you do not answer ?

Satti said...

i implemented SD card with Atmega32 in my project work successfully with your original post. now i want to improve DATA transfer speed (>3.125 Mbps) can you suggest how can be improve. can you suggest me how can i use 4 bit SD MODE?or anyother way to improve speed?

thanks

ROBOTRONICS with AVI said...

hi
your code is great. please help me with some problem.
please tell me what changes in the code i need to make if i am using a 16 MHz external crystal with atmega16.
I am ok with the fuse bit. I want to know for the changes in the code i will have to make

CC Dharmani said...

@Majid
Hi Majid,
the project you mentioned needs some hardware at my side to experiment. I can't write code and give it to you without having hardware.

For the SD card module, you can use the method of using microSD card adapters which are available easily everywhere. Check out my previous post: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

@Satti
You can first try to use 16MHz instead of 8 MHz. The SD protocol is not available free of cost. So, you need to use SPI protocol if you don't wanna buy the license for using SD protocol.

@Robotronics
for 16 MHz, no need to change anything in the code. Just double the baudrate in the HyperTerminal which you are using to communicate. i.e. it's 19200 with 8 MHz, make it 38400 when you use 16 MHz. Rest of the things will fit in automatically.

Mohan kumar said...

What is the sampling time for this data logger

Anonymous said...

Can I use TQFP instead DIP case, connecting coresponding pins like DIP ADC0 Pin40 to TQFP ADC0 Pin37 ?

Anonymous said...

Hi! Can i use this without RTC? If it`s possible, could you please tell me what i have to change?
Thanks!

sushant katiyar said...

why hear using IC DS1307???

sushant katiyar said...

why hear using DS1703? what is the function of this IC?

Anonymous said...

great job

if you like microcontrollers and everything related to it see thic link

http://all-about-embedded.blogspot.com/

Unknown said...

great job..can you give the list of components used??

Poorvaj Subbu said...

i am trying to build a board with Atmega1281 for my 16 channel datalogger pls provide me support

Anonymous said...

In file FAT.c you compare
if(mbr->signature != 0xaa55) in function getBootSectorData.. Why? Isnt correct siganture 0x55aa?

Best regards

Anonymous said...

In file FAT.c you compare
if(mbr->signature != 0xaa55) in function getBootSectorData.. Why? Isnt correct siganture 0x55aa?

Best regards

CC Dharmani said...

@Anonymous:
The storage in the FAT follows little endian format i.e. the least significant byte is stored first. The AVR also follows little endian format. Here, in mbr structure, 'signature' is declared as an integer, a two byte variable. Hence, when you read this two bytes from memory as an integer, the compiler automatically rearranges the number and puts the first byte as least significant byte and second byte as most significant. So, 0x55 and 0xaa become 0xaa55 as an integer (a single number). That's why while comparing also we need to compare mbr->signature with 0xaa55 and not with 0x55aa, even though in memory, 0x55 is stored first, followed by 0xaa.
you can get more insight by searching on net about 'little endian' and 'big endian' difference.

Anonymous said...

Should I then change all comparison of integer variables beacuse I am working with 8051 uC in C.

avi said...

Hello Sir,
Your work is worth admiring, can you please add a tutorial on micro SD card interface with atmega32 for freshers?

CC Dharmani said...

@Avi:

you can go thru the references given at the end of my earlier post: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

That will give you a lot of info for better understanding the SD card interfaces and FAT32 structure.

Daniyal said...

Hello sir, I have been trying your project for quite a while now. but the problem is that when the menu appears on the hyper terminal, and i set the date it says date set failed, and similarly for setting the time it says time setting failed. i want to know what might be the problem. i have used DS1307 in the same manner as your schematic says and as most of the other projects on the internet say.

Anonymous said...

Hi I have a problem when I write a file, I got Error in getting cluster,
I looked into the sd card and I found a sector with data followed by a blank sector, and the next with data, so what could be the solution?

Anonymous said...

I have run some test, and I found a problem when '\n' is the last character in sector, then in the next writeFile appears Error in getting cluster. I'm trying to solve the problem but I need to study the code in depth.

Amar Gupta said...

hello sir,
i want change the baud rate of the source code of 2.3 v code.

thanku sir.

emrah a said...

Hello Dharmani,
Can you refresh the project files?
They don't exist now.
Thank you very much :)

CC Dharmani said...

@Anonymous:
I'll study the code see if I can recreate the problem you're facing.

@Amar:
You can change the baudrate by changing UBRR register values in the UART_init function. Please refer to the datasheet for the value of these registers depending on the baudrate you need.

@Emrah:
The files are still existing, I just checked. I think there must be some at your internet connection or may be browser side, since earlier also you couldn't download files from my other post.

Admin CcN said...

Hi I solved the problem, I've been studing the code and I found that in the writeFile function, a comparison if(i >= 512), when data=='\n' and i=512, the code doesn't update the End Of File and other things. So thats why in the next writeFile I got Error in getting the cluster.
I tried to write files in a kingston sd card (1gb) but I couldn't, sd_fat_ini() doesn't recognize it.
I would like to know how to create folders and put files there, do you have any references?

sushant said...

sir i use IC DS1307 for RTC but time is always same in the exel file after recording, no increment in time date and time fixd ,what is the problem here?why time dose not change....

sushant said...

sir i use IC DS1307 for RTC but time is always same in the exel file after recording, no increment in time date and time fixd ,what is the problem here?why time dose not change....

CC Dharmani said...

@Sushant:
Either the DS1307 crystal (32KHz) or its battery (3.3v cell) is not connected properly. Check out the connections there.

sushant said...

sir i have change the battery and even change crytal but prob remaning same ,date and time is constant printed on data file, if cell is remove then date shows 1/1/2000.

CC Dharmani said...

Are you able to change the date/time using the terminal? If the date/time is changing when you update on terminal, but then it remains constant in the log, then it's the crystal problem. The DS1307 is not getting the clock from the crystal and hence, it's not able to count the seconds up. You need to check the crystal, the connections of the crystal with IC and may be the IC itself.

sushant said...

sir can i store the continously received rx pin data in to excel file??

YJ said...

Hi Sir, is the MAX232 and the DS1307 needed if i am not looking to set the time? Will it affect the operation of the entire system without the digital clock? Is it a need in order for the MicroSD to store values? Sorry if the question sounds noob because i am a beginner.

YJ said...

And which specific AVR ISP Programmer do you use?

Anonymous said...

hi sir,

what is the maximum sampling rate i can record the data? Right now i am using Atmega128 with internal clock frequency 8MHZ (sony SDHC card, 4x class). Now for one writefile() function it takes nearly 50ms. Is it possible to reduce it to less than 10 ms.

Satish Kumar said...

Hi great work.
I am trying to simulate this project with Proteus 7.7v.But i am facing problem with .mmc image file.Could u please suggest me how to create it and link with SD card. Whether the image should also be a FAT32 image.
I actually tried by creating a image file with winimage software.But it is still not working.
please help me as i don't own a Hardware to implement it.

vetdoktor said...

Hello! in what mistake? The damaged file if to write down more than 30 minutes. Blocks for 30 minutes after disk restoration vanish. Memory card of 1 Gb. SDHC. I don't know what to do.

Alfeus Sunarso said...

Hello,

We are developing a data logger using ATMega128 that stores data into an SD card. We start by porting your code. After minor modifications (comment out "GICR = 0x00;", replace UCSR with UCSR0, and modify the main function such that all messages displayed on LCD instead of PC terminal - code fragment attached), we were able to compile the project successfully. Unfortunately, when we run the program on the device to read a 2GB SD card formatted on a PC using FAT16 file system, it cannot detect the card and ends up with "Unknown card" message. Could you give us some clues on the problem? Anyone who successfully ported the code for ATMega128, please share your experience.
Thank you very much in advance.

Regards,
Sunarso


*******************************

int main(void)
{
unsigned char option, error, i, j, data, channel;
unsigned char fileName[13], bufstring[100];
unsigned int delay, k;

_delay_ms(100); //delay for VCC stabilization

//Initialize LCD module
LCDInit(LS_BLINK|LS_ULINE);
//Clear the screen
LCDClear();
//Simple string printing
LCDWriteString("SmartDAQ System");
LCDWriteStringXY(0,1,"TimPenelitiMP3EI");

LCDClear();
//Simple string printing
LCDWriteString("Init device ...");
init_devices();
LCDWriteStringXY(0,1,"Successful!");

cardType = 0;

LCDClear();
//Simple string printing
LCDWriteString("Init card ");
for (i=0; i<10; i++)
{
LCDWriteStringXY(14,0,(char)i);
error = SD_init();
if(!error) break;
}
LCDWriteStringXY(0,1,"Successful!");


if(error)
{
LCDClear();
if(error == 1) LCDWriteString("Card not detected!");
if(error == 2) LCDWriteString("Init failed!");
}

LCDClear();
switch (cardType)
{
case 1:
LCDWriteString("Standard (Ver 1.x).");
break;
case 2:
LCDWriteString("High Capacity.");
break;
case 3:
LCDWriteString("Standard (Ver 2.x).");
break;
default:
LCDWriteString("Unknown Card.");
break;
}

error = getBootSectorData (); //read boot sector and keep necessary data in global variables
if(error) LCDWriteStringXY(0,1,"FAT32 not compatible!"); //FAT32 incompatible drive
return 0;
}

CC Dharmani said...

Hi Alfeus,
I'm late in responding, but better to have it cleared. This code is written only for FAT32 cards. If your card is formatted with FAT16, this won't work.

Unknown said...

sir can you tell me why you are using avr_isp connector in this

Eugen said...

hallo CC
in my project I'm using yur work as base, but i change to ATmega324P at 3,3Volt so i had to change the RTC to M41T81S. now i have a problem, my clock registers are little bit different and my year register is at 07h, so after every reset i overwrite the year by zeros.
i cant find there you write the control byte at ds1307, because its the 07h register.
can you please tell me there do you write the control register of ds1307!!!
efrick@gmx.de

Ashoka Banhatti said...

Dear Sir,
This has reference to the Software for the SD Card DATALOGGER SD_Datalogger_ver1.0.
I am using 2GB SD CArd. The System works fine. The *.CSV file is created properly. But if the file size exceeds 450k or so I am not sure exactly)
the file is no more readable in PC ( i am reading the file using the Card Reader in PC).

In the same card if I give another file name then again it stores upto 450k and then is no more readable.
Can you please tell me why is it so...

CC Dharmani said...

@Eugen:
The RTC control registers are written by the functions RTC_writeTime() and RTC_writeDate() in the RTC_routines.c file. The sequence for writing these registers is as follows:
- user enters the date and time using the terminal of PC
- these date and time are stored in the strings date[ ] and time[ ] in the main function
- from these strings, the array rtc_register[ ] is updated. These array registers are also individually defined as macros SECONDS, MINUTES etc. in the RTC_routines.h, for easier understanding
- values from the array registers are written to RTC registers by calling functions RTC_writeDate and RTC_writeTime in RTC_routines.c

CC Dharmani said...

@Ashoka:
I'll test the code on my circuit for the error you are encountering. Let me know the INTERVAL that you defined in main function.

Ashoka Banhatti said...

The Interval defined is 1000 which means 1 sec. I am getting the records every 1 sec. The file 01042013.csv is created and appended. the only problem is when it exceeds 400k or so.. ( i have correct file upto 386k and the file when it is 410k it is nonreadable.)
In the PC through Card Reader when I try to open the file ( 416K) then it says " Cannot open g:\01042013 file. Make sure a disk is in the drive you specified." "the file or the directory \01042013 is corruptand unreadable. Please run the chkdsk utility.". I tried to run the chkdsk but still I cannot read the file.

Ashoka Banhatti said...

I thought if the file exceeding 400k is creating a problem, i should make multiple files. I gave the limit to the file and after 250k(i.e 2500records as one record is 100 bytes ) i changed the name of the file. The various files are created but again i get the same problem. The files are not readable. Is it that the scanning is fast( Interval=1000 i.e 1sec).

CC Dharmani said...

@Ashoka:
No, the interval is not a problem here.
I'll be able to test this problem on my circuit only on this weekend. Let me knwo if you solve it by then. It'll help.

Ashoka Banhatti said...

Sir, did you find time to look into the problem.. For smaller size files the program is working ok. but i am not able to find out when the file gets corrupted. the AVR program does not give any error but when i try to open the file through card reader the file is not readable.. this happens after 400k or so. if you can give me some hints as to where to look for the bug i can try.. i tried but not getting anywhere.. please help.

ramesh dhoju said...

in program we have made certain changes i.e.file name is taken from array.We have displayed every steps on LCD so that it will be easy for debugging.It shows that file is created but no file is made in SD card.So please suggest us for solving this problem. Our modified code is given below:



int main(void)
{
unsigned char option, error, i, j, data, channel;
unsigned char fileName[13];
unsigned int delay, k;
unsigned name[4]={'k','a','l','u'};


init_devices();


LCDWriteString("microSD Datalogger");
_delay_ms(100);
LCDClear();

l1: error = SD_init();
if(error)
{
if(error == 1)LCDWriteString("SD card not detected..");
if(error == 2) LCDWriteString("Card Initialization failed..");
PORTC= 0xfe; //lcd led as error indicator
_delay_ms(1000);
goto l1;
}
else
{
LCDWriteString("sd card DD");
_delay_ms(1000);
LCDClear();
}

error = getBootSectorData (); //read boot sector and keep necessary data in global variables
if(error)
{
LCDWriteString("\n\rFAT32 not found!"); //FAT32 incompatible drive
PORTC= 0xfe; //lcd led as error indicator
_delay_ms(1000);
LCDClear();

}
else
{
LCDWriteString("FAT detected");
_delay_ms(1000);
LCDClear();
}

SPI_HIGH_SPEED; //SCK - 4 MHz
_delay_ms(1); //some delay for settling new spi speed



LCDWriteString("writing"); //FAT32 incompatible drive
PORTC= 0xfe; //lcd led as error indicator
_delay_ms(100);
LCDClear();

for(i=0; i<4; i++)

{
fileName[i] = name[i];
LCDWriteString("in loop");
_delay_ms(1000);
LCDClear();


}

fileName[4] = '.';
fileName[5] = 'C';
fileName[6] = 'S';
fileName[7] = 'V';


//From here onwards, gather data by appending strings in dataString
//dataString is declared in FAT32.h
//make sure dataString doesn't exceed its MAX_STRING_SIZE, defined in FAT32.h
//Also, end the data string with '\r' & '\n' characters to maintain CSV format

LCDWriteString("out loop");
_delay_ms(1000);
LCDClear();
for(i=0; i<4; i++) dataString[i] = name[i];
dataString[i++] = ',';

LCDWriteString("1st str ");
_delay_ms(1000);
LCDClear();

for(j=0;j<4; j++) dataString[i++] = name[j];
dataString[i++] = ',';

LCDWriteString("2nd str ");
_delay_ms(1000);
LCDClear();
dataString[i++] = '\r';
dataString[i] = '\n'; //always end the string with these two characters,
//before calling the writeFile function
LCDWriteString("checking ");
_delay_ms(1000);
LCDClear();
error = writeFile(fileName);
LCDWriteString("erroring");
_delay_ms(1000);
LCDClear();


if(error)
{ LCDWriteString("writing failed");

_delay_ms(1000);
LCDClear();
}
else
{
LCDWriteString("wrote");
_delay_ms(1000);
LCDClear();
}

}



Anonymous said...

http://www.onlinetps.com/shop/index.php?main_page=product_info&cPath=75_20&products_id=618
is it possible to interface using this module??p

Roghib said...

mr dharmani i want to ask why no file created in sd card. i connected pin sd card:
cs(PB1)
mosi(PB5)
sck(PB7)
miso(PB6)

Anonymous said...

Why is rhe dara corrupted after a few kb's?

Anonymous said...

Sorry.. why is the data corrupted after writing few kbytes?

Anonymous said...

Is there a fix for this code?

Anonymous said...

And why when i power up the ckt the card is not recognized. I have to press the reset button a few times for it to be recognize..... can anybody help....

Anonymous said...

Hello
I want to save .csv file that are longer and have 15 characters instead of 11 what changes do I have to make in the fat32.h file ?

Thanks In Advance

Anonymous said...

Can anybody help... why data is corrupted after few kbytes?

Anonymous said...

I have thesame problem with corrupted files...

Anonymous said...

Sir, please help me in designing data logger which consists of temp, humidity,light intensity(ldr),and some keys like event logger.
should I design it using avr or 8051 cotroller.
Which one is suitable .
Please reply me.
Email ID- pavanpanchal4@gmail.com.

Pavan Panchal said...

hi. I want to built data logger using avr atmega 16, rtc ,lcd ,3sensors .
I want to store the recorded data in controllers internal EEPROM memory
i am not using external EEPROM memory . So please help me.

Pavan Panchal said...

hi. I want to built data logger using avr atmega 16, rtc ,lcd ,3sensors .
I want to store the recorded data in controllers internal EEPROM memory
i am not using external EEPROM memory . So please help me.

Mthuong said...

Hi you..time is always same in the exel file after recording, no increment in time date and time fixd
. no problem with crytal 37768, and batery 3V...
Please help me.. thansk you very much

Havizul ST said...

Thanks for this post, it is very helpfull. I have some questions :

1. I am using an mmc module for arduino. It only 6 pins : Vcc, Gnd, Miso, Mosi, sck, and without cs or cd (i forget). How can i use this module to work for this project ? I used micro sd + adapter to connect it to the module.

2. Beside sandisk what is another type of micro sd can used in this project ?

3. And as in the picture above, you used sandisk, what class is it ? Can i use sandisk with 8Gb of size and class 10 ?

Thanks.

zn. idloo said...

hi
i make your project,
but i dont have microsd madual.
i read your page about mmc or fat32.
if i make it, i can use it instead microsd maduale?
my email is :
idloo.zn@gmail.com

Anonymous said...

hi
i want to make the hex file of this source code can u plz tell me how to make the hex file.
please mail me asap on
sumayyatariq@hotmail.com

Rahul said...
This comment has been removed by the author.
Rahul said...

Sir,
I am using ur FAT32 library. My program works fine. In my project I doesn't have RTC. so I define fixed time and date array as 05/05/2009 and time as 05:05:05 day Monday. Now as per your program the 05052009.csv file has to be created. the file is also created. but when I connect the SD card in PC, I couldn't see the file. I am using 2GB microSD card. My card is detected by program as "Standard (Ver 2.x)."

When I see the file via hyperterminal as per option 5, I could see that the file is present. So Kindly provide me where I am making mistake? Waiting for your reply.

Anonymous said...

Muchas gracias por publicar tu proyecto, lo probé con un ATmega324p y, luego de hacer adaptaciones para el ese modelo, funcionó muy bien.

Anonymous said...

Somebody knows why is the data corrupted after writing few kbytes?

andycrofts said...

Hello, "CC"
I jave a commercial project that could shave a couple of weeks off design time if I could use some of Your code.
May I use some routimes, if I accredit You for it?
With thanks
Andy Crofts, Oulu, Finland
andrew.crofts@ehp-tekniikka.fi

Jayachandra Reddy said...
This comment has been removed by the author.
Jayachandra Reddy said...

sir,
i am doing project "attendance data logger with atmega 2560", for this my application is using 4x4 matrix keypad, 16x2 lcd, sd card and rtc,
first we will create all the students numbers as a folders in sd card, while student entering in to class they will enter their number on keypad that time the "in time&date" will save on their particular folder in sd card, while they exiting from class they will enter their number on keypad again this time"out time" will save on their folder in sd card,
how to do this please give me a suggestions

jayachandra1043@gmail.com

Jatin Sharma said...

Sir, I try to run and build program in Atmel Studio version6.2 , its is showing 48 errors. All functions like sp-init(), transmit etc. are showing errors. Please help.

buxi fosoyo said...
This comment has been removed by the author.
buxi fosoyo said...

Hello.
Maybe you have this project in Proteus?
I draw the circuit in Proteus, starts, but constantly
produces an error - "Card Initialization failed .."
Maybe not properly connected ...
Please contact us help.
Thanks in advance.
Good day...

P.S. Screen scheme: _http://s010.radikal.ru/i311/1412/2c/607329e0f8be.png_

buxi fosoyo said...
This comment has been removed by the author.