how use the MPU 6050 in ultra low power mode - sleep
I'm currently trying to set up a fermentation specific gravity monitor, using a tilt sensor. The process can take several weeks, and must be contained in a sterile container, so must be battery powerered. I'm using a slightly modified ESP8266-01, which enters sleep mode then wakes once an hour to take a measurement, transmit the data, and return to sleep mode. I'm using an MPU6050 for the tilt sensor. Firstly, I can't seem to put the mpu into sleep mode when the esp is off, it always seems to take around 4mA, and secondly, I only need one axis, is it possible to disable everything else to limit power consumption further? I can't seem to find anything in the manual to disable axis, only to calibrate them. my code is below
experimenting with the registers below seem to make no difference, adding them, taking them out altogether, still takes around 4mA. Tried setting to 1 to put the mpu to sleep at the end of the cycle but makes no difference.
Wire.write(0x6B);
Wire.write(0);
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650. Any advice would be greatly appreciated!
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "Wire.h"
// Update these with values suitable for your network.
const char* ssid = "****";
const char* password = "******";
IPAddress server(192, 168, 1, 90);
WiFiClient espClient5;
PubSubClient client(espClient5);
long lastMsg = 0;
char msg[50];
const uint8_t scl = 5; //D1
const uint8_t sda = 4; //D2
int val;
int prevVal = 0;
String pubString;
char gravity[50];
MPU6050 mpu;
const int sleepTimeS = 10; //only 10 seconds for testing purposes, set to
1hr when operational
int counter=0;
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) { //not
required in this application
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "test";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("AliveRegister", "FermentMon");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
#define ONE_WIRE_BUS 2 // D4 on physical board
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
float prevTemp = 0;
void setup() {
counter = 0;
Serial.begin(9600);
Wire.begin(0,2);
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(0); // set to zero wakes teh 6050
Wire.endTransmission(true);
delay(100);
setup_wifi();
client.setServer(server, 1883);
client.setCallback(callback);
if (!client.connected()) {
reconnect();
}
Serial.println("Initialize MPU");
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connected" : "Connection failed");
float temp;
DS18B20.requestTemperatures();
temp = DS18B20.getTempCByIndex(0); // first temperature sensor
char buff[100];
dtostrf(temp, 0, 2, buff);
temp = temp + 0.5;
int tRound = int(temp);
client.publish("Fermenter/temperature", buff);
Serial.print("Fermenter Temperature: ");
Serial.println(temp);
prevTemp = tRound;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
val = map(ax, -17000, 17000, 0, 180);
pubString = String(val);
pubString.toCharArray(gravity, pubString.length() + 1);
client.publish("Fermenter/angle", gravity);
Serial.print("Gravity angle: ");
Serial.println(val);
delay(500);
// counter = counter+1;
Serial.println("sleep mode");
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(1); // set to zero wakes teh 6050
// sleep
ESP.deepSleep(sleepTimeS * 1000000);
delay(2000);
}
void loop() {
client.loop();
}
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
Setting a bit is simple.
Use the follow functions to avoid any brain storming.
// Write register bit
void writeRegisterBit(uint8_t reg, uint8_t pos, bool state)
{
uint8_t value;
value = readRegister8(reg);
if (state)
{
value |= (1 << pos);
}
else
{
value &= ~(1 << pos);
}
writeRegister8(reg, value);
}
// Write 8-bit to register
void writeRegister8(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(MPU_addr);
#if ARDUINO >= 100
Wire.write(reg);
Wire.write(value);
#else
Wire.send(reg);
Wire.send(value);
#endif
Wire.endTransmission();
}
Example Usage: writeRegisterBit(MPU6050_REG_INT_PIN_CFG, 5, 1); //Register 37;Interrupt Latch Enable
For your application:
void acclSetSleepEnabled(bool state)
{
writeRegisterBit(MPU6050_REG_PWR_MGMT_1, 6, state);
}
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650
To enter low power accelerometer mode use the following function:
void lowPowerAccel(uint8_t frequency) {
uint8_t value;
value = readRegister8(MPU6050_REG_PWR_MGMT_2);
value &= 0b00111000;
value |= (frequency << 6) | 0b111;
writeRegister8(MPU6050_REG_PWR_MGMT_2, value);
value = readRegister8(MPU6050_REG_PWR_MGMT_1);
value &= 0b10010111;
value |= 0b00111000;
writeRegister8(MPU6050_REG_PWR_MGMT_1, value);
}
This lowPowerAccel function also puts the gyro to standy mode. The function needs a wake up frequency parameter.
This is defined as follows:
/*
* LP_WAKE_CTRL | Wake-up Frequency
* -------------+------------------
* 0 | 1.25 Hz
* 1 | 2.5 Hz
* 2 | 5 Hz
* 3 | 10 H
*/
#define LP_WAKE_CTRL_1_25 0x00
#define LP_WAKE_CTRL_2_5 0x01
#define LP_WAKE_CTRL_5 0x02
#define LP_WAKE_CTRL_10 0x03
I hope, I could answer some of your questions.
Good luck! :)
Are you using a breakout board for the MPU6050? e.g. GY-521. Often they use linear regulators and leds which will consume additional power. It may be necessary to remove these and run the IMU from a direct power source.
Each register in the MPU6050 is 8 bits wide. When setting an individual bit to a desired value you can either use bitwise manipulation (not practical here as we aren't directly interacting with the registers) or directly set all of the bits in the register to the register's new state e.g. 0b00100000 ~ 0x20. Instead of writing a 1 to 0x6B when attempting to put the MPU6050 to sleep you should be writing 0x20.
https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
Referencing page 40-42, if you want to take things a step further you can disable the temperature sensor, accelerometers, and redundant gyroscope axes to save power while the device is active.
Related
How to perform integrals on an arduino
I am new to arduino and I am trying to make a program that calculates the percentage of charge remaining in a battery, using the coulomb countig method (below a picture with the formula). Is it possible to perform this type of calculation from an arduino?
1.) The Model Assuming CBat is the capacity of a battery and constant (physicist prefer letter Q for electrical charge; CBat may decrease with aging, "State of Health") By Definition: SOC(t) = Q(t)/CBat Differential Equation: dQ/dt = I(t) Approximation: "dt=1" Q(t)-Q(t-1) ~= I(t) Or SOC(t) ~= SOC(t-1) + I(t)/Cbat 2.) Ardunio: Following is a pure virtual script, online compiler would not serve. // Assume current coming from serial port const float C_per_Ah = 3600; // signal adjustment const float current_scale_A = 0.1; // Ampere per serial step const int current_offset = 128; // Offset of serial value for I=0A float CBat_Ah = 94; /* Assumed Battery Capacity in Ah */ float Cbat_C = CBat_Ah * C_per_Ah; /* ... in Coulomb */ float SOC = 0.7; /* Initial State of Charge */ int incomingByte = current_offset; // for incoming serial data, initial 0 Ampere float I = 0; /* current */ // the setup routine runs once when you press reset: void setup() { Serial.begin(9600); // opens serial port, sets data rate to 9600 bps } // the loop routine runs over and over again forever: void loop() { delay(1000); // wait for a second if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read(); } I = (incomingByte-current_offset) * current_scale_A; SOC = SOC + I/Cbat_C; Serial.print("New SOC: "); Serial.println(SOC, 4); }
How to code for DHT11, pH Sensor with i2c 16x2 LCD in Arduino Uno?
I'm working on my college project on which I have to measure the temperature show it on 16x2 LCD and also to switch the cooling device according to the temperature, also I have to use pH Sensor and have to display its value on 16x2. Currently I'm using the code below for Temperature and switching, but it is not working properly. It shows the temperature on the screen but also some garbage value, and also not switching properly. #include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #include <dht.h> dht DHT; #define DHT11_PIN 7 LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); const int ledPin = 6; void setup() { lcd.setBacklightPin(3,POSITIVE); lcd.setBacklight(HIGH); lcd.begin(16, 2); lcd.clear(); Serial.begin(9600); for (int DigitalPin = 8; DigitalPin <= 8; DigitalPin++) { pinMode(DigitalPin, OUTPUT); } } void loop() { int chk = DHT.read11(DHT11_PIN); float temp=(DHT.temperature); float Hum=(DHT.humidity); lcd.print("Temp C "); lcd.setCursor(6,0); lcd.print(temp); lcd.setCursor(0,1); lcd.println("Humid % "); lcd.setCursor(6,1); lcd.print(Hum); delay(1000); if (temp <= 29) digitalWrite(8, LOW); else if (temp>30) digitalWrite(8, HIGH); }
I will divide my answer into two parts: Hardware: Please describe your hardware configuration in order to narrow down your problem: How are you switching the relay? Is it optically coupled? Are you using a transistor? If so, what type? Does your relay have reverse diode protection? (Do not connect your relay directly to an Arduino pin) Code 2.1. This for-loop in your setup() is incorrect: for (int DigitalPin = 8; DigitalPin <= 8; DigitalPin++) { pinMode(DigitalPin, OUTPUT); } You are indexing from 8 to 8, hence you don't need a for loop to declare a single pin as an output. 2.2. In general, you can assign your output pins to variables, or as a macro like you did for the DHT. Just be consistent. Additionally, your code needs general tidying up: a . Start your code with all libraries calling #include <Wire.h> // The Wire.h library is already called by the LiquidCrystal_I2C library, so you don't need to call it if you use the I2C one #include <LCD.h> // You are using the LCD library... #include <LiquidCrystal_I2C.h> // but also the I2C lcd library. You don't need both. #include <dht.h> // Is this the latest version? Use Adafruit's b. Try to group all your macros at the beginning. Declare pins or constants here: // Using macros #define DHT11_PIN 7 #define DigitalPin 8 #define DHT11_PIN 7 or using constants for pins. Choose one, not both: // Group all your const int together, for the sake of clarity const int DHT_PIN = 7; const int DigitalPin = 8; const int ledPin = 6; c. I believe this constructor is not correct for the library you chose. Check the example from this library (assuming it is the same, which I believe it is) LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); try with this constructor, instead: // Set the LCD address to 0x27 for a 16 chars and 2 line display LiquidCrystal_I2C lcd(0x27, 16, 2); d. The setup() function needs some changes too: void setup() { // You don't seem to be using the serial library. However, if you use it, it is // preferable to start it first. Serial.begin(9600); // LCD configuration lcd.begin(16, 2); // Always call the begin() method first lcd.setBacklightPin(3,POSITIVE); // Then, use additional configuration methods. lcd.setBacklight(HIGH); lcd.clear(); // Once again, please check if you are using the right library. // It might be the cause of the garbage you see. // Initialize the type of pin pinMode(DigitalPin, OUTPUT); } e. According to Adafruit's library (please see the examples), you are missing a Macro to declare the type of DHT sensor you are using: #define DHTTYPE DHT11 f. In your main loop() you are reading values with some strange parenthesis: int chk = DHT.read11(DHT11_PIN); float temp=(DHT.temperature); float Hum=(DHT.humidity); which, according to the latest library, should be: // The latest library does not have this prototype: int chk = DHT.read11(DHT11_PIN); What is it for in your dht lib? float temp = DHT.readTemperature(); // instead of: float temp=(DHT.temperature); --> which should be DHT.temperature(); in any case float hum = DHT.readHumidity(); // instead of: float Hum=(DHT.humidity); TL;DR: Share your hardware in order to assist you. Update your libraries, clean-up your code, and use the right methods from the latest libraries.
Thankyou so much for your guidance, below is the working code for me: #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <DHT.h> #define DHTPIN 7 //digital pin for sensor #define DHTTYPE DHT11 // DHT 11 DHT dht(DHTPIN, DHTTYPE); LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); void setup() { pinMode(8, OUTPUT); //for configuring relay output lcd.begin(16, 2); dht.begin(); } void loop() { delay(1000); float h = dht.readHumidity(); // Read temperature as Celsius (the default) float t = dht.readTemperature(); // Read temperature as Fahrenheit (isFahrenheit = true) float f = dht.readTemperature(true); // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t) || isnan(f)) { lcd.print("NO CONNECTION"); return; } //Hum code lcd.setCursor(0, 0); lcd.print("Hum:"); lcd.print(h); lcd.print(" % "); //Temp code lcd.setCursor(0, 1); lcd.print("Temp:"); lcd.print(t); lcd.print(" C"); //To operate relay { if (t <= 26) digitalWrite(8, LOW); else if (t>27) digitalWrite(8, HIGH); } }
how to use ultrasonic sensor hc sr04 and gps neogps 6m and gsm sim900 on arduino uno
I’m trying to send an sms with the location when the ultrasonic sensor measures a certain distance but when I add the gpsSerial(9600) line in the void setup, the ultrasonic sensor readings become inconsistent such as it reads 24 cm then skips to 18 cm then back to 24 cm. i would really appreciate it if anyone familiar with arduino or has faced this problem before would help me out. here is my code: #include <TinyGPS.h> #include <SoftwareSerial.h> #include "NewPing.h" #define TRIGGER_PIN 7 #define ECHO_PIN 8 #define MAX_DISTANCE 100 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); float duration, distance; SoftwareSerial gsmSerial(2, 3); SoftwareSerial gpsSerial(5, 4); TinyGPS gps; //Creates a new instance of the TinyGPS object bool newData = false; void setup() { Serial.begin(9600); gsmSerial.begin(9600); gpsSerial.begin(9600); } void loop() { // Send ping, get distance in cm distance = sonar.ping_cm(); if (distance < 4 ) { //sendSMS(); Serial.println("Bin is full!!, SMS SENT!!"); Serial.println(distance); delay(1200); } else { Serial.println(distance); delay(2000); } delay(2000); } void sendSMS(){ // For one second we parse GPS data and report some key values for (unsigned long start = millis(); millis() - start < 1000;) { while (gpsSerial.available()) { char c = gpsSerial.read(); //Serial.print(c); if (gps.encode(c)) newData = true; } } if (newData) //If newData is true { float flat, flon; unsigned long age; gps.f_get_position(&flat, &flon, &age); gsmSerial.print("AT+CMGF=1\r"); delay(400); gsmSerial.println("AT + CMGS = \"+26xxxxxxxx\"");// recipient's mobile number delay(300); gsmSerial.print("BIN IS FULL!, PLEASE COLLECT # https://www.google.com /maps/?q="); gsmSerial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6); gsmSerial.print(","); gsmSerial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6); delay(200); gsmSerial.println((char)26); // End AT command with a ^Z, ASCII code 26 delay(2000); gsmSerial.println(); } }
I've tried to reproduce your problem on Arduino Uno with HC SR04, but with no success – my measurements were stable (more than 100 same measurements in a row). I have even set the sensor to 24 cm distance from the measured obstacle. I used a code derived from what you have provided: #include <SoftwareSerial.h> #include "NewPing.h" #define TRIGGER_PIN 7 #define ECHO_PIN 8 #define MAX_DISTANCE 100 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); float duration, distance; SoftwareSerial gsmSerial(2, 3); SoftwareSerial gpsSerial(5, 4); void setup() { Serial.begin(9600); gsmSerial.begin(9600); gpsSerial.begin(9600); } void loop() { // Send ping, get distance in cm distance = sonar.ping_cm(); if (distance < 4 ) { Serial.println("Bin is full!!, SMS SENT!!"); Serial.println(distance); } else { Serial.println(distance); } delay(2000); } My conclusion is your code is fine and the problem is HW related. Check the wiring and if possible, try to use another HC-SR04 sensor. BTW1: During testing, I was sending data to gpsSerial (rx) just to be sure, it is not caused by the GPS module sending some periodic data. BTW2: How do you power the GPS and GSM module? Are you using Arduino to power these? If yes, this also can be the source of your problems since these modules could drain large peak currents and Arduino as source is not powerful enough ...
RPM meter and send value with serial communication
I have a question about how to reading RPM and send value with serial It's my code: char init(void) { UBRRH=(uint8_t) (UBRR_CALC>>8); UBRRL=(uint8_t) UBRR_CALC; UCSRB=(1<<RXEN)|(1<<TXEN); UCSRC=(1<<URSEL)|(3<<UCSZ0); return 0; } void send(unsigned char x) { while(!(UCSRA&(1<<UDRE))){} UDR=x; } void sendstring(char *s) { while(*s) { send(*s); s++; } } volatile uint16_t count=0; //Main revolution counter volatile uint16_t rps=0; //Revolution per second void Wait() { uint8_t i; for(i=0;i<2;i++) { _delay_loop_2(0); } } int main(void) { Wait(); Wait(); Wait(); Wait(); //Init INT0 MCUCR|=(1<<ISC01); //Falling edge on INT0 triggers interrupt. GICR|=(1<<INT0); //Enable INT0 interrupt //Timer1 is used as 1 sec time base //Timer Clock = 1/1024 of sys clock //Mode = CTC (Clear Timer On Compare) TCCR1B|=((1<<WGM12)|(1<<CS12)|(1<<CS10)); //Compare value=976 OCR1A=976; TIMSK|=(1<<OCIE1A); //Output compare 1A interrupt enable //Enable interrupts globaly sei(); while(1) { } } ISR(INT0_vect) { //CPU Jumps here automatically when INT0 pin detect a falling edge count++; } ISR(TIMER1_COMPA_vect) { //CPU Jumps here every 1 sec exactly! rps=count; send(rps); count=0; } Can we send serial data when interrupt counter and timer, i have error when try it ? And what is the best methode to read RPM and send value with serial
One potential problem is that rps is defined as int16_t and your send routine is expecting an unsigned char. Is the range 0-255 high enough for your RPS? Do you want the RPS to be human readable or are you sending it to another computer or micro-controller? It is possible to send serial in you interrupt routines but you need to be careful since the serial routines are not usually re-entrant.
ATMega2560 PCINT Interrupts
i am new to Atmel, so maybe the question is quite simple. I have the following situation: I have an ATMega2560 and want to get Interrupts on the Pins PK0-PK7. I am interested in the PIN Change from Low to HIGH. (I have connected one photocell to every PIN) I have read that the interrupt for PCINT[0-2] are fired everytime (pin high and pin low) so i defined an array to ignore the PIN DOWN Interrupt. So i have the following code: Note: I do not understand, why i need to set DDRC as input and not DDRK ? #define HIGH 1 #define LOW 0 volatile uint8_t portbhistory = 0xFF; uint8_t pinState[8]; void initSystem() { int i; for(i = 0; i < sizeof(pinState) / sizeof(*pinState); i++) { pinState[8] = 0; } DDRB = 0xff; // set Port B as output DDRC = 0; // WHY DDRC ?? PCINT == PK0-7, so DDRK ? // all Pins input PORTC = 0xff; // same question ... // turn on pullup for every pin PCICR |= _BV(PCIE2); // enable interrupt for PCIE2 PCMSK2 = 0xff; // Interrupt at all pins sei(); // turn on interrupts } int main(void) { initSystem(); while(1) { } } ISR(PCINT2_vect) { int i; uint8_t changedbits; changedbits = PINC ^ portbhistory; portbhistory = PINC; /* if(pinState[changedbits] == HIGH) { pinState[changedbits] = LOW; return; } else { pinState[changedbits] = HIGH; } */ setDebugLED(1); _delay_ms(30); setDebugLED(0); } If i connect 5V to one of the PINS (PK0-7) the LED flashes instantly. But if i disconnect the 5V it takes ~2seconds for the LED to flash again. In this time the one pin i connected 5V before does not let the LED flash again if i connect the 5 V again. Other Ports work in this time. So you could say, that the PIN HIGH interrupt for all pins work fine and get fired instantly but the PIN LOW interrupts need some time (~2sec.) In this time the port is "disabled" or somethig. Can anyone help me with that? EDIT Just some wrong edit ... EDIT 2 So, i totally forgot to post my solution here. sorry for that! The solution is: My external circuit pulls the PIN to Ground or to 5V. Thank you!