I try to build a data logger with SD card for saving sensor data. I need to reduce the power consumption as soon as the circuit is going to sleep. The problem is the power consumption of the SD card module of about 3mA. I read a lot about saving power and many do switch of the power to the SD card module with re-initializing the card when waking up. I can't achieve that. As soon as the SD card module is switched of only error messages are thrown. Can anybody give me a hint to put me on the right track? How do I re-initialize the SD card module right?
Thanks
1. Edit
Everything works fine until the first wake up. Than the code error message "Card failed, or not present" is thrown and the loop will start again without writing to SD card.
here is what I got so far:
// DHT sensor library
#include "DHT.h"
// SD card library
#include <SD.h>
// for sleep modes
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#define DHTPIN 9
#define DHTTYPE DHT22 //DHT11, DHT21, DHT22
DHT dht(DHTPIN, DHTTYPE);
// make sure that the default chip select pin is set
const int chipSelect = 4;
int counter = 0;
int sdPower = 8;
volatile int sleepcounter = 0; // count sleep cycles
void setup() {
pinMode(sdPower, OUTPUT);
// output, even if you don't use it to ensure proper SD library working:
pinMode(10, OUTPUT);
digitalWrite(sdPower, LOW);
watchdogOn(); // switch on Watchdog timer
ADCSRA = ADCSRA & B01111111; // switch off ADC, ADEN bit7 zu 0
ACSR = B10000000; // switch off analog Comparator, ACD bit7 to 1
DIDR0 = DIDR0 | B00111111; // switch off digital input buffer, analog input pins 0-5 to 1
dht.begin();
}
// -------------------------------------- LOOP ---------------------------------
void loop() {
Serial.begin(9600);
// ------------------------- initialize SD card -------------------------
digitalWrite(sdPower, HIGH);
Serial.println("Start of recording");
delay(500);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
delay(100);
// don't do anything more:
return;
}
Serial.println("card initialized.");
// --------------------------------------------------------------------------
float humidity = dht.readHumidity(); //measure humidity
float temp = dht.readTemperature(); //measure temp
// make a string for assembling the data to log:
String dataString = "";
// ------------------------- read sensor and store in string -----------------------------
// check for valid number, throw error for NaN (not a number)
if (isnan(temp) || isnan(humidity)) {
Serial.println("no read for DHT22");
}
else {
dataString += "MP-";
dataString += String(counter);
dataString += ",";
dataString += String(temp);
dataString += ",";
dataString += String(humidity);
// ------------------------- open SD card and write values -----------------------------
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
delay(1000);
File dataFile = SD.open("datalog.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile) {
dataFile.println(dataString);
dataFile.close(); //dataFile.sync() doesn't change something
delay(500);
// print to the serial port too:
Serial.println(dataString);
delay(500);
counter = counter + 1;
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
//-------------------------------------- sleep mode activation ----------------------------
// Stay awake for 0.5 second, then sleep.
delay(500);
digitalWrite(sdPower, LOW);
delay(500);
pwrDown(5); // go to sleep for (x) sec.
}
}
// some methods for sleep mode are not shown
The often used SD.hlibrary is not able to manage powered down SD card modules. After the power is taken away the code will throw an error. The card can't get re-initialized.
I used the SdFat.h instead and it works just perfect. SD cards draw a lot of current. To switch the SD card module a MOSFet is recommended.
Related
i want to record a long data for like 2 to 3 hours using microcontroller Arduino Mega 2560 WI-FI R3 but I got this warning in my code
below is the picture of the warning i got.
enter image description here
the warning come up when I put the time out constant more than 30000 milliseconds
below is the code I used for the Arduino
#include "DHT.h"
// Pin Definitions
#define DHT_PIN_OUT A1
#define DHTTYPE DHT11
#define MQ3_PIN_OUT A2
#define MQ4_PIN_OUT A3
#define MQ7_PIN_OUT A4
// Global variables and defines
// object initialization
DHT dht(DHT_PIN_OUT, DHTTYPE);
// define vars for testing menu
const int timeout = 180000; //define timeout of 1 hour
char menuOption = 0;
long time0;
// Setup the essentials for your circuit to work. It runs first every time your circuit is powered with electricity.
void setup()
{
// Setup Serial which is useful for debugging
// Use the Serial Monitor to view printed messages
Serial.begin(9600);
while (!Serial) ; // wait for serial port to connect. Needed for native USB
Serial.println("start");
dht.begin();
menuOption = menu();
}
// Main logic of your circuit. It defines the interaction between the components you selected. After setup, it runs over and over again, in an eternal loop.
void loop(){
if(menuOption == '1') {
delay(500); //delay 0.5 second
// DHT11 Humidity and Temperature Sensor
// Reading humidity in %
float dhtHumidity = dht.readHumidity();
// Read temperature in Celsius, for Fahrenheit use .readTempF()
float dhtTempC = dht.readTemperature();
float Alcohol = analogRead(MQ3_PIN_OUT);
float Methane = analogRead(MQ4_PIN_OUT);
float CarbonMonoxide = analogRead(MQ7_PIN_OUT);
Serial.print(F("Humidity: ")); Serial.print(dhtHumidity); Serial.print(F("[%]\t"));
Serial.print(F("Temp: ")); Serial.print(dhtTempC); Serial.print(F("[C]\t"));
Serial.print(F("Alcohol: ")); Serial.print(Alcohol); Serial.print(F(" \t"));
Serial.print(F("Methane: ")); Serial.print(Methane); Serial.print(F(" \t"));
Serial.print(F("Carbon Monoxide: ")); Serial.println(CarbonMonoxide); Serial.println(F(" \t"));
}
if (millis() - time0 > timeout){
menuOption = menu();
}
}
// Menu function for selecting the components to be tested
// Follow serial monitor for instrcutions
char menu(){
Serial.println(F("\nSensor Array"));
Serial.println(F("Press (1) to start the sensor array"));
while (!Serial.available());
// Read data from serial monitor if received
while (Serial.available()){
char c = Serial.read();
if (isAlphaNumeric(c))
{
if(c == '1')
Serial.println(F("Now running the sensor array"));
else{
Serial.println(F("illegal input!"));
menuOption = menu();
return 0;
}
time0 = millis();
return c;
}
}
}
sorry for my bad programming and silly question, I'm new to this kind of thing. and I just found out that people usually get answers from StackOverflow, so I just wanted to try asking because I have tried to google the answer but I can't find it.
I've built simple project with my daughter that takes two temp readings and writes the to a file on a Micro SD card.
The code is fairly simple (if not a bit messy) and is creating the file, but only ever seems to write the same row of data twice (from the setup()) and then never writes again, although doesn't seem to produce an error.
The code is:
#include <Wire.h> //Required for Realtime Clock
#include "RTClib.h" //Required for Realtime Clock
#include <SPI.h> //Required for SD card reader
#include <SD.h> //Required for SD card reader
#include <dht11.h> //Required for Temp/humidity reader
#define DHTPIN 4 //Pin for temp sensor
#define LEDPIN 7 //Pin for led (status)
#define DHTTYPE DHT11 //define which temp sensor we are using (DHT11)
dht11 DHT11; //Create instance of DHT11 class to read sensor values
File myFile;
String filename;
uint32_t start_min; //Store time the current loop started so we can work out how long we've been running for
RTC_DS3231 RTC; //Create instance of real Time clock class
void setup() {
Serial.begin(9600); //start serial monitor for output
Wire.begin();
//begin the rtc, let us know if it fails
if (! RTC.begin()) {
Serial.println("Couldn't find RTC");
abort();
}
Serial.print("Initializing SD card...");
if (!SD.begin(10)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
//set RTC time and date to the time the app was compiled
RTC.adjust(DateTime(__DATE__, __TIME__));
//Get the current RTC time and sore it as a unix time
DateTime now = RTC.now();
start_min = now.unixtime();
//Setup Filename
String filename = String(start_min);
int start = filename.length() - 8;
int end = filename.length();
filename = filename.substring(2,10) + ".csv";
Serial.println(filename);
//Write Header row
myFile = SD.open(filename, FILE_WRITE);
if(myFile) {
myFile.println("timestamp|humidity|temperature1|temperature2");
myFile.flush();
myFile.close();
delay(1000);
}
else {
Serial.println("Error opening file");
}
}
void loop() {
//Get current RTC date and time
DateTime now = RTC.now();
//Convert to unix time
uint32_t _now = now.unixtime();
// work out if x mins have passed
if((_now - start_min) > (4*60)) {
//Turn LED on
digitalWrite(LEDPIN,HIGH);
Serial.print("Looped - ");
Serial.print(now.hour());
Serial.print(":");
Serial.println(now.minute());
int chk = DHT11.read(DHTPIN);
myFile = SD.open(filename, FILE_WRITE);
if(myFile) {
String output = String(now.getTimeStr());
output += "|";
output += String((float)DHT11.humidity,2);
output += "|";
output += String((float)DHT11.temperature, 2);
output += "|";
output += String(RTC.getTemperature());
Serial.println(output);
myFile.println(output);
myFile.close();
Serial.println("Write complete");
}
else {
Serial.println("Error opening file");
}
//reset start time
start_min = now.unixtime();
}
delay(5000);
//Turn LED off
digitalWrite(LEDPIN,0);
//wait 90 secs
delay(5000);
}
I'm using an Arduino Uno, a DHT11 for temp/humidity and a DS3231 RTC (also grabbing temp there to).
Once compiled and uploaded. this is the output:
Sketch uses 16798 bytes (52%) of program storage space. Maximum is 32256 bytes.
Global variables use 1290 bytes (62%) of dynamic memory, leaving 758 bytes for local variables. Maximum is 2048 bytes.
any help appreciate
I have this code that I am using to play a sound effect where I used a program called wav2c to convert a .wav file to number values that I put into a header file that I use in the code to generate the sound. I currently have it programmed to play the audio upon uploading it to the Arduino with an LED being activated along with it and staying lit for just the duration of the sound effect. I am trying to program it so that the sound and LED only activate when I am pressing a button. I have the pin the button is plugged into already programmed in but I'm not sure how to have it control the audio and LED as stated above. I don't have much experience with programming or Arduino so any help is much appreciated! I am using an Arduino Mega 2560.
The code
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 20000
#include "Test.h"
int ledPin = 2;
int speakerPin = 9; // Can be either 3 or 11, two PWM outputs connected to Timer 2
const byte pinSwitch1 = 3;
volatile uint16_t sample;
byte lastSample;
void stopPlayback()
{
digitalWrite(ledPin, LOW);
// Disable playback per-sample interrupt.
TIMSK1 &= ~_BV(OCIE1A);
// Disable the per-sample timer completely.
TCCR1B &= ~_BV(CS10);
// Disable the PWM timer.
TCCR2B &= ~_BV(CS10);
digitalWrite(speakerPin, LOW);
}
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
if (sample >= sounddata_length) {
if (sample == sounddata_length + lastSample) {
stopPlayback();
}
else {
if(speakerPin==11){
// Ramp down to zero to reduce the click at the end of playback.
OCR2A = sounddata_length + lastSample - sample;
} else {
OCR2B = sounddata_length + lastSample - sample;
}
}
}
else {
if(speakerPin==11){
OCR2A = pgm_read_byte(&sounddata_data[sample]);
} else {
OCR2B = pgm_read_byte(&sounddata_data[sample]);
}
}
++sample;
}
void startPlayback()
{
pinMode(speakerPin, OUTPUT);
// Set up Timer 2 to do pulse width modulation on the speaker
// pin.
// Use internal clock (datasheet p.160)
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
// Set fast PWM mode (p.157)
TCCR2A |= _BV(WGM21) | _BV(WGM20);
TCCR2B &= ~_BV(WGM22);
if(speakerPin==11){
// Do non-inverting PWM on pin OC2A (p.155)
// On the Arduino this is pin 11.
TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2A = pgm_read_byte(&sounddata_data[0]);
} else {
// Do non-inverting PWM on pin OC2B (p.155)
// On the Arduino this is pin 3.
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2B = pgm_read_byte(&sounddata_data[0]);
}
// Set up Timer 1 to send a sample every interrupt.
cli();
// Set CTC mode (Clear Timer on Compare Match) (p.133)
// Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
// No prescaler (p.134)
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
OCR1A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000
// Enable interrupt when TCNT1 == OCR1A (p.136)
TIMSK1 |= _BV(OCIE1A);
lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
sample = 0;
sei();
}
void setup()
{
pinMode( pinSwitch1, INPUT );
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
startPlayback();
}
void loop()
{
while (true);
}
The header file referenced in the code with the numeric values to create the audio.
#ifndef _HEADERFILE_H // Put these two lines at the top of your file.
#define _HEADERFILE_H // (Use a suitable name, usually based on the file name.)
const int sounddata_length=32000;
//const int sounddata_sampleRate=20000;
const unsigned char sounddata_data[] PROGMEM = {
15,1,49,0,150,0,138,0,219,255,133,0,176,0,15,1,210,
//There are many lines of more numbers in between that I cut out to save space
};
#endif // _HEADERFILE_H // Put this line at the end of your file.
The following changes will allow you to start playback whenever there is a falling edge on your switch pin. You may need to tweak to avoid switch 'bouncing'.
Firstly, add a global variable to record the last switch state:
int lastSwitchState;
Change your setup() to
void setup() {
pinMode(pinSwitch1, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
lastSwitchState = digitalRead(pinSwitch1);
}
and your loop() function to
void loop() {
delay(50);
int switchState = digitalRead(pinSwitch1);
if (switchState != lastSwitchState) {
lastSwitchState = switchState;
if (switchState == LOW) {
startPlayback();
}
}
}
Interrupts vs polling
Instead of polling the switch pin inside the main loop(), you could use interrupts. You would use attachInterrupt() to do this. Interrupts are only available on certain pins, however, and the above approach is conceptually simpler I think.
I have a sketch running a MFRC522 RFID reader which works fine but my aim is to log the card swipes on a microSD card.
The problem is that as soon as I add the SD card reader, whether it is initialised or not, the RFID reader stops working.
It seems to be a problem on the SPI bus. I have tried adding pull up resistors to the circuit and setting the chip select pins to HIGH before initialising either of the boards but nothing seems to work.
Here is my code:
#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>
// RFID constants & objects
#define RFPin 10
#define resetPin 9
MFRC522 mfrc522(RFPin, resetPin);
// SD constants
#define SDPin 8
// Other global variables
String IDString;
byte IDList[4];
void setup() {
pinMode(2, OUTPUT);// For testing
// Set both chip select pins high
pinMode(SDPin, OUTPUT);
digitalWrite(SDPin, HIGH);
pinMode(RFPin, OUTPUT);
digitalWrite(RFPin, HIGH);
delay(10);
// Init serial bus
Serial.begin(9600);
// Wait for serial bus to open
while (!Serial);// Opens even when not USB connected
// Init SPI bus
SPI.begin();
// Initialise RFID board
mfrc522.PCD_Init();
delay(1000);// Just in case SPI is still busy
// Initialise SD card board
if (!SD.begin(SDPin)) {
// SD card board failed to initialise
Serial.println("SD failed");
return;
}
}
void loop() {
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
for(int i = 0; i < 4; i++) {
digitalWrite(2, HIGH);
delay(100);
digitalWrite(2, LOW);
delay(100);
}
IDString = "";
// Get 4 byte ID
for (int i = 0; i < 4; i++) {
IDList[i] = mfrc522.uid.uidByte[i];
// Serial.print(IDList[i], HEX);
if(IDList[i] < 16) {
IDString += "0" + String(IDList[i], HEX) + "-";
} else {
IDString += String(IDList[i], HEX) + "-";
}
}
mfrc522.PICC_HaltA(); // Stop reading
IDString = IDString.substring(0, IDString.length() - 1);
Serial.println(IDString);
.
.
.
Rest of code
Everything initialised ok but Once in the main loop, the program never gets past the first statement: to check if an RFID card is present.
Can anybody help?
Could it be de to the fact that the RFDI board is 3.3V driven and the SD board is 5V driven (but has a 3.3V regulator)? Each has it's own separate power wire
EDIT #1:
When the MicroSD board is plugged in but not powered the RFID board works fine
I have also tried adding a subroutine to pull both chip select pins high at the beginning of every loop to no avail.
EDIT #2:
The SD board works in this sketch and I can get the card details from it.
EDIT #3:
The RFID card works again once I remove the MISO line from the SD board.
Obviously the SD module isn't releasing the MISO line...
It's not the most elegant solution but I have it working now by attaching an NPN transistor between the MISO output of the SD board and the MISO line to pin 12. It takes one further pin to block/unblock the MISO line to be used by the SD board but it works.
As I said this is not elegant and I would still be eager to hear a better solution.
What a great learning experience my first Arduino project is turning out to be.. I would now like to add a countdown until a sensor reading is taken and displayed, which will repeat infinitely. I've got the sensor and LCD display working fine but my loop is not quite right.. Should I be using a while() of some sort? How do I keep the timer ticking during the big delay between readings?
/*Code for self-watering plant with LCD readout*/
// value for LCD params
char ESC = 0xFE;
// analog input pin that the soil moisture sensor is attached to
const int analogInPin = A1;
// value read from the soil moisture sensor
int sensorValue = 0;
// if the readings from the soil sensor drop below this number, then turn on the pump
int dryValue;
// countdown timer until next soil reading
int timerValue = 9;
void setup() {
pinMode(12, OUTPUT);
// initialize serial communications at 9600 bps:
Serial.begin(9600);
// Set the "dry" value of soil on turning on the device
dryValue = analogRead(analogInPin);
// pause before intialize LCD
delay(2000);
// Initialize LCD module
Serial.write(ESC);
Serial.write(0x41);
Serial.write(ESC);
Serial.write(0x51);
// Set Contrast
Serial.write(ESC);
Serial.write(0x52);
Serial.write(40);
// Set Backlight
Serial.write(ESC);
Serial.write(0x53);
Serial.write(5);
//print the dry value to serial
Serial.print("Dry = " );
Serial.print(dryValue);
Serial.print(" ");
}
void loop(){
watering();
// wait some time (really should be delay(86400000))
delay(10000);
}
void printTimer(){
// Set cursor line 1, column 16
Serial.write(ESC);
Serial.write(0x45);
Serial.write(0x0F);
// print the timer value
Serial.print(timerValue);
timerValue = timerValue - 1;
if(timerValue == 0){
timerValue = 9;
}
}
void printVal(){
// set cursor line 2, column 1
Serial.write(ESC);
Serial.write(0x45);
Serial.write(0x40);
// print the sensor to the serial monitor:
Serial.print("Sensor = " );
Serial.print(sensorValue);
Serial.print(" ");
printTimer();
}
void watering(){
// read the analog in value:
sensorValue = analogRead(analogInPin);
//turn on the water pump for some time if the soil is too dry
if(sensorValue < dryValue){
digitalWrite(12, HIGH);
delay(2000);
digitalWrite(12, LOW);
}
else {
printVal();
}
}
It's actually really simple: Don't delay. Instead, initialize a timer to run a routine whenever it overflows or hits a certain value. Examine the datasheet for the microcontroller used in your Arduino for the specific bits to frob (note that the Arduino libraries use the timer 0 overflow vector for themselves), and the avr-libc documentation for how to denote the ISR(s) for the timer. Your loop() then becomes a big sleep while the timer runs the entire show for you.
I would use a timer library for Arduino like this http://playground.arduino.cc//Code/SimpleTimer
Just download the library, put it in the "libraries" folder in your sketchbook and restart your Arduino IDE to load the new library.
Then your code would look something like this. Basically what it does it updates the screen every loop and then once every 86400000 ms it checks the "watering" function. Just so you know this code would only check the soil once every 24 hours (86400000ms). I think a better solution would be to constantly check the soil and water anytime it is needed. But Im no gardener so maybe there is a reason for just checking once a day.
#include <SimpleTimer.h>
// the timer object
SimpleTimer timer;
void setup() {
Serial.begin(9600);
timer.setInterval(86400000, watering); // how often you would call your watering function is set with the first variable
}
void loop() {
timer.run();
printTimer();
}
void printTimer(){
// Set cursor line 1, column 16
Serial.write(ESC);
Serial.write(0x45);
Serial.write(0x0F);
// print the timer value
Serial.print(timerValue);
timerValue = timerValue - 1;
if(timerValue == 0){
timerValue = 9;
}
}
void printVal(){
// set cursor line 2, column 1
Serial.write(ESC);
Serial.write(0x45);
Serial.write(0x40);
// print the sensor to the serial monitor:
Serial.print("Sensor = " );
Serial.print(sensorValue);
Serial.print(" ");
printTimer();
}
void watering(){
// read the analog in value:
sensorValue = analogRead(analogInPin);
// send it to the display
printVal();
//turn on the water pump for some time if the soil is too dry
if(sensorValue < dryValue){
digitalWrite(12, HIGH);
delay(2000);
digitalWrite(12, LOW);
}
}