How to perform integrals on an arduino - arduino-uno

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);
}

Related

What is the proper way of integrating a function in parts using boost odeint?

I have some code that generates an acoustic timeseries using a 2nd order 1 dimensional differential equation.
I decided to use boost::odeint to integrate the equations, but because this is being used ina real-time simulation, I need to generate a small number of steps repeatedly.
The code is of the following form:
class Integrator
{
public:
typedef std::vector<double> state_type;
//This is how boost::odeint wants it to look
/* NOTE: We have to cast our second order diff eq into a set of first-order diff eqs
because ODEINT only handles first order ODEs.
*/
void operator() ( const state_type &x , state_type &dxdt , const double t )
{
dxdt[0] = x[1];
//... Bunch of calculations here to determine dxdt and ddxdt
dxdt[1] = ddxdtTerm;
}
state_type x_state;
state_type dxdt_state;
std::vector<double> x_buffer;
std::vector<double> time_buffer;
double time;
};
struct IntegrationObserver
{
std::vector<double> & xVals;
std::vector<double> & tVals;
IntegrationObserver(std::vector<double> & in_xVals,
std::vector<double> & in_tVals) :
xVals(in_xVals),
tVals(in_tVals)
{
}
void operator()( const std::vector<double> &x , double t )
{
xVals.push_back( x[0] );
tVals.push_back( t );
}
};
I then have an acoustics generation thread that generates 100ms of acoustic data once every tenth of a second (at 22050 Hz):
std::vector<Integrator> g_intVec;
void audioBufferGeneratorThread()
{
std::cout << "Hello from the audio buffer generator thread!" << std::endl;
Uint32 lastTime = SDL_GetTicks();
Uint32 thisTime = lastTime;
size_t acousticBufsize = 0.1 * 22050;
float* acousticWorkBuffer = new float[acousticBufsize];
memset(acousticWorkBuffer, 0, acousticBufsize * sizeof(float));
while(!g_terminating)
{
bool didWork = false;
//Check time elapsed, have we seen 0.10 seconds pass?
thisTime = SDL_GetTicks();
double t_add = (thisTime - lastTime) / 1000.0;
//For each integrator
if(t_add >= 0.1){
t_add = 0.1;
lastTime = thisTime;
didWork = true;
{
std::lock_guard<std::mutex> guard(g_integratorMutex);
int bubIdx = 0;
for(auto integrator : g_intVec)
{
//Only generate a maximum 1 second of acoustics for each integrator...
//Main thread deletes integrators once they reach
if(integrator.t_sec >= 1.0)
{
continue;
}
size_t steps;
boost::numeric::odeint::runge_kutta4<BubbleIntegrator::state_type> stepper;
//Generate some audio
steps = boost::numeric::odeint::integrate_const(stepper,
integrator,
integrator.x_state,
integrator.t_sec,
integrator.t_sec + t_add,
1.0/22050.,
integratorObserver(integrator.x_buffer, integrator.time_buffer );
integrator.t_sec += t_add;
bubIdx++;
}
//Sum all the audio together
g_integratorMutex.unlock();
}
{
// std::lock_guard<std::mutex> guard(g_audioBufferMutex);
//Push the audio to the audio buffer
}
memset(acousticWorkBuffer, 0, acousticBufsize * sizeof(float));
}
//Yield the thread if we didn't generate any acoustics.
else
{
std::this_thread::yield();
}
}
delete acousticWorkBuffer;
std::cout << "Goodbye from the audio buffer generator thread..." << std::endl;
}
As one can see, this code is meant to store the previous X and DXDT states, as well as the current time, so that the integrator can pick up where it left off later.
However, the problem appears to be that the integrator is calculating from t=0 every time, so each advance through the acoustics generator loop takes longer and longer until the code is no longer able to generate the acoustics in realtime (i.e. generate 100ms of acoustics in under 100ms of code time).
It appears that I'm using the odeint library incorrectly, but I am unable to find a proper example of how to do what I'm trying to do here: generate the first 100ms of data, then the next 100ms, then the next, etc...
So my question is thus:
How can I use boost::odeint to generate sequential chunks of the equation without it starting from t=0 each time?

How to read multiple ADC channels using PIC24; can only get AN0

I am using a PIC24 to read data using 3 analog inputs but am only getting 1 to show the right result. I looked everywhere on the internet and am still not able to get the code to work.
I am trying to read 3 analog input signals and am only able to read in AN0.
I am using an accelerometer to obtain the data and show it on the LCD screen for now. I was able to implement 3 different ways to take the data and display it but only an0 works and an1 is not the right value.
void InitADC(int amask) {
AD1PCFG = 0xFFF8; // select AN0, AN1, AN2 as analog inputs
AD1CON1 = 0x00E0; // auto convert # end of sampling, Integer Data out.
// see Text pg. 179 & Sec. 17 on AD1CON1.
//AD1CON2bits.CSCNA = 1;
AD1CON3 = 0x1F01; // Tad = 2xTcy = 125ns. 31*Tad for conversion time.
//AD1CSSL = 0xFFF7; // Scan 3 channels
AD1CON1bits.ADON = 1; // Turn on the ADC
} // InitADC
main() {
InitADC(0xFFF8); // initialize the ADC and analog inputs
char x_string [12];
char y_string [12];
char z_string [12];
//TRISB = 1; // all PORTB pins as outputs
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
TRISBbits.TRISB2 = 1;
InitPMP(); // Initialize the Parallel Master Port
InitLCD(); // Initialize the LCD
float x_val;
float y_val;
float z_val;
float x_axis, y_axis, z_axis;
while (1) // main loop
{
x_axis= SelectPort(0);
x_val= ((((x_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(x_string, "X: %0.2f ", x_val);
ms_delay(2.5);
y_axis= SelectPort(1);
y_val= ((((y_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(y_string, "Y: %0.2f ", y_val);
ms_delay(2.5);
z_axis= SelectPort(2);
z_val= ((((z_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(z_string, "Z: %0.2f ", z_val);
ms_delay(2.5);
}
Here is the code where the data is read:
int SelectPort(int ch)
{
//int *result;
AD1CON1bits.ADON = 0; // Turn off the ADC to reconfigure
//result = &ADC1BUF0;
switch(ch) // set values based on the channel to use
{
case 0: // select AN0 as analog input
//AD1CHSbits.CH0SA=0;
//result = ADC1BUF0;
AD1PCFG = 0xFFFE;
break;
case 1:
//AD1CHSbits.CH0SA=1;
//result = ADC1BUF1;
AD1PCFG = 0xFFFD; // select AN1 as analog input
break;
case 2:
//AD1CHSbits.CH0SA=2;
AD1PCFG = 0xFFFB; // select AN2 as analog input
break;
// there's only so many options here, so there's not really a default case
}
AD1CON1bits.ADON = 1; // Turn on the ADC
AD1CHS = ch; // 1. select analog input channel
AD1CON1bits.SAMP = 1; // 2. Start sampling.
while (!AD1CON1bits.DONE); //5. wait for conversion to complete
AD1CON1bits.DONE = 0; // 6. clear flag. We are responsible see text.
return ADC1BUF0; // 7. read the conversion results
}
I am new to PIC24 and need help to figure out why I am not able to get multiple ADC channels to read the data.
You should always give some time for the ADC input voltage to settle after changing its input port. Also, make sure the impedance of your inputs signals are less than 10K.
Separating the ADC channel switching and reading its value will help, and give you an opportunity to add a delay.
I could not find AD1PCFG in the PIC24 datasheet. AD1PCFG is a PIC32 register... PIC24 uses ANSx and TRSx to set pins as analog inputs. Analog inputs should be set once, during the boot sequence. Feeding an analog signal to a digital input CMOS pin will result in increased power draw, and can even lead to hardware failure!
// Set the pins as analog inputs once and for all in your setup code.
void InitADC()
{
// select RB0/AN0, RB1/AN1, RB2/AN2 as analog inputs
TRISB |= 0x07;
ANSB |= 0x07;
// ...
}
void ADC_SelectInput(int ch)
{
AD1CHS = ch & 0x0F
}
int ADC_Read()
{
AD1CON1bits.SAMP = 1;
while (!AD1CON1bits.DONE)
;
AD1CON1bits.DONE = 0;
return ADC1BUF0;
}
Your loop then becomes:
// ...
int adc_in;
while (1)
{
ADC_SelectInput(0);
ms_delay(3); // settling delay, you could also do something
// else in the meantime instead of waiting.
// NOTE: the argument to ms_delay() is an integer
// so waiting at least 2.5 ms makes it 3.
// when computing floats, use float constants, not double.
x_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(x_string, "X: %0.2f ", x_val);
ADC_SelectInput(1);
ms_delay(3);
y_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(y_string, "Y: %0.2f ", y_val);
ADC_SelectInput(2);
ms_delay(3);
z_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(z_string, "Z: %0.2f ", z_val);
}

PLL turning on when debugging on STM32F767 Nucleo

I have been scratching my head for the past 2 days trying to figure out what is wrong with my very simple UART program on System Workbench for STM32 from STMicroelectronics. For some reason when I set:
USART3->BRR = (16000000/9600); //Sets the baud rate to 9600 with a clock of 16MHz
The output on the serial line is rubbish, however when I press the reset button on the board and let it run, I see
Hello World
As expected. Doing some digging I found out that the source for the system clock is the PLL output (RCC_CFGR register), red arrow on the imagem bellow:
RCC_CFGR
The bits correspondent to the red arrows tells me that the PLL is ON. Analyzing the RCC_PLLCFGR register and inputing the PLLM, PLLN and PLLP parameters togheter with the correspondents prescallers (AHB and APB1) into the CubeMX I arrived at a APB1 bus clock of 48MHz. So I did:
USART3->BRR = (48000000/9600); //Sets the baud rate to 9600 with a clock of 48MHz
And voilà doing step by step on the debugger I can see "HELLO WORLD" so the bus clock is 48MHz.
BUT, and I press the reset button and leave it running now the output is non-sense. Something on the debugging process is turning on the PLL and using it as the SYSCLK source.
I copied and pasted the code into Keil uVision and there was no problem, debugging and running, so the problem isn't the code itself. I took a look at the startup_stm32.s, core_cm7.h and debug files from SW4STM32 and couldn't find any hint as to why this is happening. Looking in the reference manual Reference Manual on page 170 the reset value of RCC_CFGR is 0x0000.0000 so the PLL is TURNED OFF after reset, so something in the debugging is actively setting the PLL ON.
I am using Tera Term to look at the ST-LINK serial port and Digital Discovery from Digilent to confirm what is going on the bus, and both give me even data.
Any ideas?
Full code:
#include "stm32f767xx.h"
#include "time.h"
/*
* Function declarations
*/
void uS_Delay(int Time);
void mS_Delay(int Time);
void S_Delay(int Time);
void GPIOAa_Init(void);
void Config_TIM5(void);
void TIM5_IRQHandler(void);
void Enable_TIM5Interrupt(void);
void UART_Config(void);
void UART_Send(int character);
int Delay_End = 0;
int main(void)
{
GPIOD_Init();
Config_TIM5();
Enable_TIM5Interrupt();
UART_Config();
int butao = 0;
while(1)
{
UART_Send('H');
mS_Delay(10);
UART_Send('E');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('O');
mS_Delay(10);
UART_Send(' ');
mS_Delay(10);
UART_Send('W');
mS_Delay(10);
UART_Send('O');
mS_Delay(10);
UART_Send('R');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('D');
mS_Delay(10);
}
}
/*
* Functions
*/
void uS_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 uS clock is:
*
* Nº Steps = 1uS/62.5nS
* Nº Steps = 16
*
* So for a delay of "Time" microseconds the number of steps is:
*
* Delay = Time*16
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, in this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void mS_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16000);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 mS clock is:
*
* Nº Steps = 1uS/62.5nS
* Nº Steps = 16.000
*
* So for a delay of "Time" microseconds the number of steps is:
*
* Delay = Time*16.000
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, is this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void S_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16000000);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 S clock is:
*
* Nº Steps = 1S/62.5nS
* Nº Steps = 16.000.000
*
* So for a delay of "Time" seconds the number of steps is:
*
* Delay = Time*16.000.000
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, is this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void GPIOD_Init(void){
int a;
RCC->AHB1ENR |= 0x08; //Enables clock for port D
a = RCC->AHB1ENR; //Small delay
GPIOD->MODER |= 0x00020000;
/*
* Sets output mode for alternate mode for pin PD8
*/
GPIOD->AFR[1] |= 0x00000007;
/*
* Alternate function 7 selected for pin PD8
*/
}
void Config_TIM5(void){
int a;
RCC->APB1ENR |= 0x08; //Enables clock for TIM5
a = RCC->APB1ENR; //Small delay
TIM5->DIER |= 0X01; //Enables update interrupt
}
void TIM5_IRQHandler(void){
Delay_End = 1; //The specified delay time has ended
TIM5->SR &= ~(0x01); //Clears the update event flag
}
void Enable_TIM5Interrupt(void){
NVIC->ISER[1] = 0x40000; //Enables interrupt event from TIM5 peripheral
NVIC->IP[50] = 0x00; //Sets the priority for the TIM5 interrupt
}
void UART_Config(void){
int a;
RCC->APB1ENR |= 0x00040000; //Enable clock for UART4
a = RCC->APB1ENR; //Small delay
USART3->CR1 |= 0x08; //Enable the transmitter
USART3->BRR = (48000000/9600); //Sets the baud rate to 9600
USART3->CR1 |= 0x01; //Enable the USART4
}
void UART_Send(int character){
USART3->TDR = (character);
}
Edit1: I got confused with RCC_CR and RCC_CFGR but the problem is the same.
Edit2: I noticed that the prescalers for APB1 and APB2 buses were also changed.
Edit3: Workaround when you start the debugging session press the icon(Reset the chip and restart debug session), this button will restart the debug session with a reset. This way the contents of the registers are as expected. I am still on the look for a mre permanent solution.

how use the MPU 6050 in ultra low power mode

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.

Blink LED without delay

So I'm creating a code so that I can read and store temperature values
for at least 8hours and store it in the Arduino EEPROM. I also want the Built in LED to flash once every second, while the temperature sensor records once every minute. I wrote the following but I'm left with the LED staying on for a whole minute then off for another minute and so on. I want it to keep constantly flashing. I know it's because of my delay(6000) but I don't know how to fix it. Any help is appreciated, thanks!
#include <EEPROM.h> //Librería para controlar la EEPROM de la Arduino
float tempC; //Initialize variable for storing Temperature
int tempPin = 0; //Conected at A0
int addr = 0; //Cantidad de espacios (bytes) iniciales
int count = 0; //counter needed to stop at a certain point before overflowing the EEPROM memory
int Voltages[501]; // Array with space for 501 data Points.A little over 8 hours. Holding integer values of Voltages.
float Temps[501]; //Holds float values for Temperatures
const int SWITCH = 8; //set switch to port 8
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // blink once per second
void setup(){
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);
pinMode(SWITCH,INPUT_PULLUP);}
void loop(){
int i;
if (digitalRead(SWITCH)==HIGH & count<=501){
for (int i=0;i<EEPROM.length();i++){
EEPROM.write(i,0);} // Clears the EEPROM so that only new recorded values are shown. In case of terminating before 8h
Serial.println("-------Recording-------");
unsigned long currentMillis = millis(); // current time
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // save the last time the LED blinked
if (ledState == LOW) {// if the LED is off turn it on and vice-versa:
ledState = HIGH;} else {
ledState = LOW;}
digitalWrite(LED_BUILTIN, ledState);}
for (i=0; i<501; i++){
count = count+1;
int v = analogRead(tempPin); // reads voltage
Voltages[i] = v;
Serial.println(i);
Serial.println(Voltages[i]);
delay(60000);
EEPROM.put(addr, Voltages[i]); //stores voltages in EEPROM
addr= addr +2;}}
I think the solution you are looking for involves the use of timer interrupts. That would execute an interrupt service routine (might as well be a blinking led) regardless of what happens in the rest of your loop function. This might give you a better in-sight: Arduino timer interrupts

Resources