Advice on program construction - DMX

Getting on slowly....

I am calling a class within a class and it's not sharing the public variable (it prints the value correctly in one class, but it's zero in the other class).
Both classes have the same variable name and type declared in the public variables.

The class that collects the screen touch data. This serial.prints the correct touch data fine (ScreenData)'


ReadNEXTION.h

Code:
//

#ifndef ReadNEXTION_h                                                   // include guard. Ensures only one instance of the is library can ever be included in the sketch
#define ReadNEXTION_h
#include "Arduino.h"

class ReadNEXTION {                                                     // Define the class

  public:                                                               // Variables available in and outside of this class
    ReadNEXTION();                                                      // Constructor declaration.  Any variable declared within the brackets would be brought here from the main code when the instance of this class is declared before setup.
    void NEXTIONvalue();                                                // This is the function (what this class actually does)  Any variable declared within the brackets would be brought into that function from the main code.

    byte ScreenData;                                                    // Required value from screen
    byte ScreenDump;                                                    // Unused data

  private:                                                              // Variables only available within this class
};
#endif

ReadNEXTION.cpp

Code:
#include <Arduino.h>
#include "ReadNEXTION.h"
#include <EEPROM.h>

ReadNEXTION::ReadNEXTION() {
  Serial.begin(115200);                                                                           // Debug
  Serial7.begin(115200);                                                                          // Nextion screen comms
}

void ReadNEXTION::NEXTIONvalue() {

//------------------ Read the Nextion screen ----------------

  ScreenData = 0;

  ScreenDump = Serial7.read();//Serial.print("Data byte 1: ");Serial.println(ScreenDump);
  delay(5);
  ScreenDump = Serial7.read();//Serial.print("Data byte 2: ");Serial.println(ScreenDump);
  delay(5);
  ScreenData = Serial7.read();//Serial.print("ScreenData (byte 3): ");Serial.println(ScreenData);                 // This is the number of the button pressed

  if (ScreenData == 255) {                                                                                        // Ignore bounce readings
    ScreenData = 0;
  }
  else
  {
    Serial.print("Button pressed= "); Serial.println(ScreenData);
  }
}

This all works within itself, but the variable is not available outside this class (or it's not importing to other classes correctly).


My keypad class:


KeypadREAD.h

Code:
#ifndef KeypadREAD_h                                                    // include guard. Ensures only one instance of the is library can ever be included in the sketch
#define KeypadREAD_h
#include "Arduino.h"

class KeypadREAD {                                                      // Define the class

  public:                                                               // Variables available in and outside of this class
    KeypadREAD();                                                       // Constructor declaration.  Any variable declared within the brackets would be brought here from the main code when the instance of this class is declared before setup.
    void KEYPADvalue();                                                 // This is the function (what this class actually does)  Any variable declared within the brackets would be brought into that function from the main code.

    byte Page;                                                          // Variables for the screen
    byte ScreenDump;
    byte ScreenData;

    byte entereddigits;                                                 // Variables for the keypad
    int keypadnumber;
    int keypad100;                                                      // Digits converted from keypad
    int keypad10;
    int keypad1;

  private:                                                              // Variables only available within this class
};
#endif

Basic Keypadread.CPP

Code:
#include <Arduino.h>
#include "KeypadREAD.h"
#include <EEPROM.h>
#include "ReadNEXTION.h"
ReadNEXTION ReadNEXTIONcall_1;

KeypadREAD::KeypadREAD() {
  Serial.begin(115200);                                                                           // Debug
  Serial7.begin(115200);                                                                          // Nextion screen comms
}

void KeypadREAD::KEYPADvalue() {

ReadNEXTIONcall_1.NEXTIONvalue();
Serial.println((ScreenData);                      /// This returns zero

}
 
Last edited:
am I right in saying you started with one 4000 line “.ino” file and want to make your code more readable.

You could just separate the file however it makes sense and put them in seperate files and just “#include” those.
 
I was trying to learn how to use classes.
Have not worked out why it doesn't share public variables. I obviously don't understand that correctly.
 
Oh well. I have tried for hours to get these variables to 'share'
Adding extern doesn't seem to help.

I'll abandon that and go back to one huge code.
 
This is how I do it.

EDIT: ( If you look at the code pointed to in Post #8 I also use the method shown below to access the Class variables )

I have set up a .h filer below with one public variable:
C++:
#pragma once

#include <Arduino.h>

class AccessPublicVariables {

public:

    uint32_t myPublicVariable;
};

Which I have accessed using the following program:
C++:
// Visual Micro is in vMicro>General>Tutorial Mode
//
/*
    Name:       AccessPublicVariables.ino
    Created:    05/01/2024 18:52:29

*/
#include <AccessPublicVariables.h>

AccessPublicVariables publicVariables;

void setup()
{
    publicVariables.myPublicVariable = 3535;
}


void loop()
{
}
 
Last edited:
If I try that with my code in post #27

if ReadNEXTION.ScreenData == 0{}

I get a

Code:
KeypadREAD.cpp:29: error: expected unqualified-id before '.' token
   ReadNEXTION.ScreenData = 1;
              ^
 
If I try that with my code in post #27

if ReadNEXTION.ScreenData == 0{}

I get a

Code:
KeypadREAD.cpp:29: error: expected unqualified-id before '.' token
   ReadNEXTION.ScreenData = 1;
              ^
I'll have a look in the morning.
 
I have worked out the issue, but it's all getting a bit too complicated passing all this info between classes.

Thanks anyway
 
I don't think you need classes. Just put your subroutines into one or more .h files and then include them into your main program.

In my projects I often put my global variables into projNameVars.h and let's say keyboard routines into projNameKbd.h, etc. Tidies things up nicely.
Note: I only put "finished working" code into the .he's.
 
You see this is what I have failed to understand... getting old sucks.

I didn't realise (or understand) that you can have a .h file without an associated .cpp file.

How do you call just the single .h files? Think I best stick to what little I know!
 
See how briComp did it.
Code:
#include <AccessPublicVariables.h>
Because he made a file with that file name.

As far as I understand it just cooies that file and puts everything in where you place the #include line.
 
You know the ide may not care what the file extension is. Although I think if you include a “somename.h” and fill it full of function and class headers (is that the right word) and then. Use one of those functions or classes I’m thinking the compiler automatically looks for a “somename.cpp” file for the definitions of those classes or functions.
 
But when your program is big I think it makes sense to seperate headers and definitions because it makes it easier for me to look at what function prototype is without searching through heaps of code for it.
 
I have a feeling allot of the blanks in my knowledge could be filled in efficiently with a few face to face conversations but forums don’t really do it.
 
The trouble I have is I find the well over half the tutorials or `information on the internet today is outdated, doesn't work or plain incorrect.
Although I am grateful for people sharing their knowledge, it does more damage than good when you are trying to learn something that is actually been explained incorrectly or badly.

For example the Classes demo I used earlier in this post. If you copy the guys code into the IDE, it fails because it has basic formatting errors.
 
I find chat gpt quite good for this sort of stuff. When it does give me something that doesn't work right i just tell it and it fixes the issue. If you haven't used it for this sort of stuff it is quite amazing.
 
For example I just copied your post #27 into chatgpt. No changes. The answer it fired back is correct and pretty clear.. if it wasn't you could ask "hey i don't get that bit explain in more detail" and it would. Just be aware it can lie sometimes but programming stuff I don't think it really goes too far wrong.

gpt.png
 
Hmm. At this point I am so lost and confused that I think one huge code is just simpler for my small brain.

I understand you can split the code into separate ino files, but I didn't even get that to compile correctly.
I basically don't understand how to format these extra ino files. You look it up on Google and it says simply list your code on separate ino files and the IDE will combine them. Not so, you obviously need to declare the required variables etc in each ino.

I think I should back out the door slowly....
 
Ok here is a fuller example of dumping Classes and just segmenting your code in .h files.

Here is the main .ino file.
Code:
// Demonstration of how to tidy code using .h files

#include "DemoVars.h"
#include "SetUpTeensyTimeForRV3028.h"

void setup(){
    Serial.begin(9600);
    while (!Serial && (millis() < 5000));
    if (!SetUpTime()) Serial.println("Unable to set-up Teensy Ti,e Link to RV3028");        // SetUpTime is obtained from SetUpTeensyTimeForRV3028.h
};

void loop(){
    aVariable = (uint32_t)68726;    // aVariable stored/declared in DemoVars.h
};

and here is the first .h file holding a GLOBAL variable.
Code:
#pragma once
// DemoVars.h
//
// Demo using Global Variables in a .h file

uint32_t aVariable;

....and here is a .h file with some code to synch Teensy time to a RV-3028_C7 RTC:
Code:
#pragma once

/*
* Routines common to ALL devices
*/
//#include HHMActions.h

//#include <Arduino.h>
#include <TimeLib.h>        // https://github.com/PaulStoffregen/Time

#include <RTC_RV_3028_C7.h>     // https://github.com/constiko/RV-3028_C7-Arduino_Library
#include <Streaming.h>      // http://arduiniana.org/libraries/streaming/

RV3028 rtc;

static time_t getUnixTime() { return rtc.getUNIX(); }

/*------------------------------------------------------------------------
 * Setup Time - Get Time from RTC
 -------------------------------------------------------------------------*/
bool SetUpTime(bool trickleCharge = false) {

    bool isOk = true;

    if (trickleCharge) {
        rtc.enableTrickleCharge();
    }
    else
    {
        rtc.disableTrickleCharge();
    }
    setSyncProvider(getUnixTime);

    isOk = (timeStatus() != timeSet);

    if (isOk)
        Serial.println(F("Unable to sync with the RTC"));
    else
        Serial.println(F("RTC has set the system time"));

    return isOk;

};
 
Here are the files needed for the RTC which allows the above examples to compile:

RTC_RV_3028_C7.h
Code:
/******************************************************************************
RV-3028-C7.h
RV-3028-C7 Arduino Library
Constantin Koch
July 25, 2019
https://github.com/constiko/RV-3028_C7-Arduino_Library

Resources:
Uses Wire.h for I2C operation

Development environment specifics:
Arduino IDE 1.8.9

This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact constantinkoch@outlook.com.
Distributed as-is; no warranty is given.
******************************************************************************/

#pragma once

#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include <Wire.h>



//The 7-bit I2C ADDRESS of the RV3028
#define RV3028_ADDR                        (uint8_t)0x52


//REGISTERS
//Clock registers
#define RV3028_SECONDS                  0x00
#define RV3028_MINUTES                  0x01
#define RV3028_HOURS                    0x02
//Calendar registers
#define RV3028_WEEKDAY                    0x03
#define RV3028_DATE                     0x04
#define RV3028_MONTHS                    0x05
#define RV3028_YEARS                    0x06

//Alarm registers
#define RV3028_MINUTES_ALM                 0x07
#define RV3028_HOURS_ALM                   0x08
#define RV3028_DATE_ALM                    0x09

//Periodic Countdown Timer registers
#define RV3028_TIMERVAL_0                0x0A
#define RV3028_TIMERVAL_1                0x0B
#define RV3028_TIMERSTAT_0                0x0C
#define RV3028_TIMERSTAT_1                0x0D

//Configuration registers
#define RV3028_STATUS                    0x0E
#define RV3028_CTRL1                    0x0F
#define RV3028_CTRL2                    0x10
#define RV3028_GPBITS                    0x11
#define RV3028_INT_MASK                    0x12

//Eventcontrol/Timestamp registers
#define RV3028_EVENTCTRL                0x13
#define RV3028_COUNT_TS                    0x14
#define RV3028_SECONDS_TS                0x15
#define RV3028_MINUTES_TS                0x16
#define RV3028_HOURS_TS                    0x17
#define RV3028_DATE_TS                    0x18
#define RV3028_MONTH_TS                    0x19
#define RV3028_YEAR_TS                    0x1A

//Unix Time registers
#define RV3028_UNIX_TIME0                0x1B
#define RV3028_UNIX_TIME1                0x1C
#define RV3028_UNIX_TIME2                0x1D
#define RV3028_UNIX_TIME3                0x1E

//RAM registers
#define RV3028_USER_RAM1                0x1F
#define RV3028_USER_RAM2                0x20

//Password registers
#define RV3028_PASSWORD0                0x21
#define RV3028_PASSWORD1                0x22
#define RV3028_PASSWORD2                0x23
#define RV3028_PASSWORD3                0x24

//EEPROM Memory Control registers
#define RV3028_EEPROM_ADDR                0x25
#define RV3028_EEPROM_DATA                0x26
#define RV3028_EEPROM_CMD                0x27

//ID register
#define RV3028_ID                        0x28

//EEPROM Registers
#define EEPROM_Clkout_Register            0x35
#define RV3028_EEOffset_8_1                0x36    //bits 8 to 1 of EEOffset. Bit 0 is bit 7 of register 0x37
#define EEPROM_Backup_Register            0x37


//BITS IN IMPORTANT REGISTERS

//Bits in Status Register
#define STATUS_EEBUSY    7
#define STATUS_CLKF        6
#define STATUS_BSF        5
#define STATUS_UF        4
#define STATUS_TF        3
#define STATUS_AF        2
#define STATUS_EVF        1
#define STATUS_PORF        0

//Bits in Control1 Register
#define CTRL1_TRPT        7
#define CTRL1_WADA        5//Bit 6 not implemented
#define CTRL1_USEL        4
#define CTRL1_EERD        3
#define CTRL1_TE        2
#define    CTRL1_TD1        1
#define CTRL1_TD0        0

//Bits in Control2 Register
#define CTRL2_TSE        7
#define CTRL2_CLKIE        6
#define CTRL2_UIE        5
#define CTRL2_TIE        4
#define CTRL2_AIE        3
#define CTRL2_EIE        2
#define CTRL2_12_24        1
#define CTRL2_RESET        0

//Bits in Hours register
#define HOURS_AM_PM            5

//Bits in Alarm registers
#define MINUTESALM_AE_M        7
#define HOURSALM_AE_H        7
#define DATE_AE_WD            7

//Commands for EEPROM Command Register (0x27)
#define EEPROMCMD_First                    0x00
#define EEPROMCMD_Update                0x11
#define EEPROMCMD_Refresh                0x12
#define EEPROMCMD_WriteSingle            0x21
#define EEPROMCMD_ReadSingle            0x22

//Bits in EEPROM Backup Register
#define EEPROMBackup_TCE_BIT            5                //Trickle Charge Enable Bit
#define EEPROMBackup_FEDE_BIT            4                //Fast Edge Detection Enable Bit (for Backup Switchover Mode)
#define EEPROMBackup_BSM_SHIFT            2                //Backup Switchover Mode shift
#define EEPROMBackup_TCR_SHIFT            0                //Trickle Charge Resistor shift

#define EEPROMBackup_BSM_CLEAR            0b11110011        //Backup Switchover Mode clear
#define EEPROMBackup_TCR_CLEAR            0b11111100        //Trickle Charge Resistor clear
#define    TCR_3K                            0b00            //Trickle Charge Resistor 3kOhm
#define    TCR_5K                            0b01            //Trickle Charge Resistor 5kOhm
#define    TCR_9K                            0b10            //Trickle Charge Resistor 9kOhm
#define    TCR_15K                            0b11            //Trickle Charge Resistor 15kOhm


// Clock output register (0x35)
#define EEPROMClkout_CLKOE_BIT            7                //1 = CLKOUT pin is enabled. – Default value on delivery
#define EEPROMClkout_CLKSY_BIT            6
// Bits 5 and 4 not implemented
#define EEPROMClkout_PORIE                  3                //0 = No interrupt, or canceled, signal on INT pin at POR. – Default value on delivery
                                                                        //1 = An interrupt signal on INT pin at POR. Retained until the PORF flag is cleared to 0 (no automatic cancellation).
#define EEPROMClkout_FREQ_SHIFT            0                //frequency shift
#define FD_CLKOUT_32k                      0b000              //32.768 kHz – Default value on delivery
#define FD_CLKOUT_8192                    0b001             //8192 Hz
#define FD_CLKOUT_1024                    0b010              //1024 Hz
#define FD_CLKOUT_64                      0b011           //64 Hz
#define FD_CLKOUT_32                      0b100              //32 Hz
#define FD_CLKOUT_1                          0b101              //1 Hz
#define FD_CLKOUT_TIMER                    0b110              //Predefined periodic countdown timer interrupt
#define FD_CLKOUT_LOW                      0b111             //CLKOUT = LOW


#define IMT_MASK_CEIE                    3                //Clock output when Event Interrupt bit.
#define IMT_MASK_CAIE                    2                //Clock output when Alarm Interrupt bit.
#define IMT_MASK_CTIE                    1                //Clock output when Periodic Countdown Timer Interrupt bit.
#define IMT_MASK_CUIE                    0                //Clock output when Periodic Time Update Interrupt bit.


#define TIME_ARRAY_LENGTH 7 // Total number of writable values in device

enum time_order {
    TIME_SECONDS,    // 0
    TIME_MINUTES,    // 1
    TIME_HOURS,      // 2
    TIME_WEEKDAY,    // 3
    TIME_DATE,       // 4
    TIME_MONTH,      // 5
    TIME_YEAR,       // 6
};

class RV3028
{
public:

    RV3028(void);

    bool begin(TwoWire &wirePort = Wire, bool set_24Hour = true, bool disable_TrickleCharge = true, bool set_LevelSwitchingMode = true);

    bool setTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t weekday, uint8_t date, uint8_t month, uint16_t year);
    bool setTime(uint8_t * time, uint8_t len);
    bool setSeconds(uint8_t value);
    bool setMinutes(uint8_t value);
    bool setHours(uint8_t value);
    bool setWeekday(uint8_t value);
    bool setDate(uint8_t value);
    bool setMonth(uint8_t value);
    bool setYear(uint16_t value);
    bool setToCompilerTime(); //Uses the hours, mins, etc from compile time to set RTC

    bool updateTime(); //Update the local array with the RTC registers

    char* stringDateUSA(); //Return date in mm-dd-yyyy
    char* stringDate(); //Return date in dd-mm-yyyy
    char* stringTime(); //Return time hh:mm:ss with AM/PM if in 12 hour mode
    char* stringTimeStamp(); //Return timeStamp in ISO 8601 format yyyy-mm-ddThh:mm:ss

    uint8_t getSeconds();
    uint8_t getMinutes();
    uint8_t getHours();
    uint8_t getWeekday();
    uint8_t getDate();
    uint8_t getMonth();
    uint16_t getYear();


    bool is12Hour(); //Returns true if 12hour bit is set
    bool isPM(); //Returns true if is12Hour and PM bit is set
    void set12Hour();
    void set24Hour();

    bool setUNIX(uint32_t value);//Set the UNIX Time (Real Time and UNIX Time are INDEPENDENT!)
    uint32_t getUNIX();

    void enableAlarmInterrupt(uint8_t min, uint8_t hour, uint8_t date_or_weekday, bool setWeekdayAlarm_not_Date, uint8_t mode, bool enable_clock_output = false);
    void enableAlarmInterrupt();
    void disableAlarmInterrupt();
    bool readAlarmInterruptFlag();
    void clearAlarmInterruptFlag();

    void setTimer(bool timer_repeat, uint16_t timer_frequency, uint16_t timer_value, bool setInterrupt, bool start_timer, bool enable_clock_output = false);
    void enableTimer();
    void disableTimer();
    void enableTimerInterrupt();
    void disableTimerInterrupt();
    bool readTimerInterruptFlag();
    void clearTimerInterruptFlag();

    void enablePeriodicUpdateInterrupt(bool every_second, bool enable_clock_output = false);
    void disablePeriodicUpdateInterrupt();
    bool readPeriodicUpdateInterruptFlag();
    void clearPeriodicUpdateInterruptFlag();

    void enableTrickleCharge(uint8_t tcr = TCR_15K); //Trickle Charge Resistor default 15k
    void disableTrickleCharge();
    bool setBackupSwitchoverMode(uint8_t val);

    void enableClockOut(uint8_t freq);
    void enableInterruptControlledClockout(uint8_t freq);
    void disableClockOut();
    bool readClockOutputInterruptFlag();
    void clearClockOutputInterruptFlag();

    uint8_t status(); //Returns the status byte
    void clearInterrupts();

    //Values in RTC are stored in Binary Coded Decimal. These functions convert to/from Decimal
    uint8_t BCDtoDEC(uint8_t val);
    uint8_t DECtoBCD(uint8_t val);

    uint8_t readRegister(uint8_t addr);
    bool writeRegister(uint8_t addr, uint8_t val);
    bool readMultipleRegisters(uint8_t addr, uint8_t * dest, uint8_t len);
    bool writeMultipleRegisters(uint8_t addr, uint8_t * values, uint8_t len);

    bool writeConfigEEPROM_RAMmirror(uint8_t eepromaddr, uint8_t val);
    uint8_t readConfigEEPROM_RAMmirror(uint8_t eepromaddr);
    bool waitforEEPROM();
    void reset();

    void setBit(uint8_t reg_addr, uint8_t bit_num);
    void clearBit(uint8_t reg_addr, uint8_t bit_num);
    bool readBit(uint8_t reg_addr, uint8_t bit_num);
private:
    uint8_t _time[TIME_ARRAY_LENGTH];
    TwoWire *_i2cPort;
};

//POSSIBLE ENHANCEMENTS :
//ENHANCEMENT: Battery Interrupt / check battery voltage
//ENHANCEMENT: External Event Interrupt

....and RTC_RV_3028_C7.cpp
Code:
/******************************************************************************
RV-3028-C7.h
RV-3028-C7 Arduino Library
Constantin Koch
July 25, 2019
https://github.com/constiko/RV-3028_C7-Arduino_Library

Development environment specifics:
Arduino IDE 1.8.9

This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact constantinkoch@outlook.com.
Distributed as-is; no warranty is given.
******************************************************************************/

#include "RTC_RV_3028_C7.h"

//****************************************************************************//
//
//  Settings and configuration
//
//****************************************************************************//

// Parse the __DATE__ predefined macro to generate date defaults:
// __Date__ Format: MMM DD YYYY (First D may be a space if <10)
// <MONTH>                                                                   
#define BUILD_MONTH_JAN ((__DATE__[0] == 'J') && (__DATE__[1] == 'a')) ? 1 : 0
#define BUILD_MONTH_FEB (__DATE__[0] == 'F') ? 2 : 0
#define BUILD_MONTH_MAR ((__DATE__[0] == 'M') && (__DATE__[1] == 'a') && (__DATE__[2] == 'r')) ? 3 : 0
#define BUILD_MONTH_APR ((__DATE__[0] == 'A') && (__DATE__[1] == 'p')) ? 4 : 0
#define BUILD_MONTH_MAY ((__DATE__[0] == 'M') && (__DATE__[1] == 'a') && (__DATE__[2] == 'y')) ? 5 : 0
#define BUILD_MONTH_JUN ((__DATE__[0] == 'J') && (__DATE__[1] == 'u') && (__DATE__[2] == 'n')) ? 6 : 0
#define BUILD_MONTH_JUL ((__DATE__[0] == 'J') && (__DATE__[1] == 'u') && (__DATE__[2] == 'l')) ? 7 : 0
#define BUILD_MONTH_AUG ((__DATE__[0] == 'A') && (__DATE__[1] == 'u')) ? 8 : 0
#define BUILD_MONTH_SEP (__DATE__[0] == 'S') ? 9 : 0
#define BUILD_MONTH_OCT (__DATE__[0] == 'O') ? 10 : 0
#define BUILD_MONTH_NOV (__DATE__[0] == 'N') ? 11 : 0
#define BUILD_MONTH_DEC (__DATE__[0] == 'D') ? 12 : 0
#define BUILD_MONTH BUILD_MONTH_JAN | BUILD_MONTH_FEB | BUILD_MONTH_MAR | \
BUILD_MONTH_APR | BUILD_MONTH_MAY | BUILD_MONTH_JUN | \
BUILD_MONTH_JUL | BUILD_MONTH_AUG | BUILD_MONTH_SEP | \
BUILD_MONTH_OCT | BUILD_MONTH_NOV | BUILD_MONTH_DEC
// <DATE>                                                                   
#define BUILD_DATE_0 ((__DATE__[4] == ' ') ? 0 : (__DATE__[4] - 0x30))
#define BUILD_DATE_1 (__DATE__[5] - 0x30)
#define BUILD_DATE ((BUILD_DATE_0 * 10) + BUILD_DATE_1)
// <YEAR>                                                                   
#define BUILD_YEAR (((__DATE__[7] - 0x30) * 1000) + ((__DATE__[8] - 0x30) * 100) + \
((__DATE__[9] - 0x30) * 10)  + ((__DATE__[10] - 0x30) * 1))

// Parse the __TIME__ predefined macro to generate time defaults:
// __TIME__ Format: HH:MM:SS (First number of each is padded by 0 if <10)
// <HOUR>                                                                   
#define BUILD_HOUR_0 ((__TIME__[0] == ' ') ? 0 : (__TIME__[0] - 0x30))
#define BUILD_HOUR_1 (__TIME__[1] - 0x30)
#define BUILD_HOUR ((BUILD_HOUR_0 * 10) + BUILD_HOUR_1)
// <MINUTE>                                                                   
#define BUILD_MINUTE_0 ((__TIME__[3] == ' ') ? 0 : (__TIME__[3] - 0x30))
#define BUILD_MINUTE_1 (__TIME__[4] - 0x30)
#define BUILD_MINUTE ((BUILD_MINUTE_0 * 10) + BUILD_MINUTE_1)
// <SECOND>                                                                   
#define BUILD_SECOND_0 ((__TIME__[6] == ' ') ? 0 : (__TIME__[6] - 0x30))
#define BUILD_SECOND_1 (__TIME__[7] - 0x30)
#define BUILD_SECOND ((BUILD_SECOND_0 * 10) + BUILD_SECOND_1)

RV3028::RV3028(void)
{

}

bool RV3028::begin(TwoWire &wirePort, bool set_24Hour, bool disable_TrickleCharge, bool set_LevelSwitchingMode)
{
    //We require caller to begin their I2C port, with the speed of their choice
    //external to the library
    //_i2cPort->begin();
    _i2cPort = &wirePort;

    delay(1);
    if (set_24Hour) { set24Hour(); delay(1); }
    if (disable_TrickleCharge) { disableTrickleCharge(); delay(1); }

    return((set_LevelSwitchingMode ? setBackupSwitchoverMode(3) : true) && writeRegister(RV3028_STATUS, 0x00));
}

bool RV3028::setTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t weekday, uint8_t date, uint8_t month, uint16_t year)
{
    _time[TIME_SECONDS] = DECtoBCD(sec);
    _time[TIME_MINUTES] = DECtoBCD(min);
    _time[TIME_HOURS] = DECtoBCD(hour);
    _time[TIME_WEEKDAY] = DECtoBCD(weekday);
    _time[TIME_DATE] = DECtoBCD(date);
    _time[TIME_MONTH] = DECtoBCD(month);
    _time[TIME_YEAR] = DECtoBCD(year - 2000);

    bool status = false;

    if (is12Hour())
    {
        set24Hour();
        status = setTime(_time, TIME_ARRAY_LENGTH);
        set12Hour();
    }
    else
    {
        status = setTime(_time, TIME_ARRAY_LENGTH);
    }
    return status;
}

// setTime -- Set time and date/day registers of RV3028 (using data array)
bool RV3028::setTime(uint8_t * time, uint8_t len)
{
    if (len != TIME_ARRAY_LENGTH)
        return false;

    return writeMultipleRegisters(RV3028_SECONDS, time, len);
}

bool RV3028::setSeconds(uint8_t value)
{
    _time[TIME_SECONDS] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setMinutes(uint8_t value)
{
    _time[TIME_MINUTES] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setHours(uint8_t value)
{
    _time[TIME_HOURS] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setWeekday(uint8_t value)
{
    _time[TIME_WEEKDAY] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setDate(uint8_t value)
{
    _time[TIME_DATE] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setMonth(uint8_t value)
{
    _time[TIME_MONTH] = DECtoBCD(value);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

bool RV3028::setYear(uint16_t value)
{
    _time[TIME_YEAR] = DECtoBCD(value - 2000);
    return setTime(_time, TIME_ARRAY_LENGTH);
}

//Takes the time from the last build and uses it as the current time
//Works very well as an arduino sketch
bool RV3028::setToCompilerTime()
{
    _time[TIME_SECONDS] = DECtoBCD(BUILD_SECOND);
    _time[TIME_MINUTES] = DECtoBCD(BUILD_MINUTE);
    _time[TIME_HOURS] = DECtoBCD(BUILD_HOUR);

    //Build_Hour is 0-23, convert to 1-12 if needed
    if (is12Hour())
    {
        uint8_t hour = BUILD_HOUR;

        bool pm = false;

        if (hour == 0)
            hour += 12;
        else if (hour == 12)
            pm = true;
        else if (hour > 12)
        {
            hour -= 12;
            pm = true;
        }

        _time[TIME_HOURS] = DECtoBCD(hour); //Load the modified hours

        if (pm == true) _time[TIME_HOURS] |= (1 << HOURS_AM_PM); //Set AM/PM bit if needed
    }

    // Calculate weekday (from here: http://stackoverflow.com/a/21235587)
    // 0 = Sunday, 6 = Saturday
    uint16_t d = BUILD_DATE;
    uint16_t m = BUILD_MONTH;
    uint16_t y = BUILD_YEAR;
    uint16_t weekday = (d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) % 7 + 1;
    _time[TIME_WEEKDAY] = DECtoBCD(weekday);

    _time[TIME_DATE] = DECtoBCD(BUILD_DATE);
    _time[TIME_MONTH] = DECtoBCD(BUILD_MONTH);
    _time[TIME_YEAR] = DECtoBCD(BUILD_YEAR - 2000); //! Not Y2K (or Y2.1K)-proof :(

    return setTime(_time, TIME_ARRAY_LENGTH);
}

//Move the hours, mins, sec, etc registers from RV-3028-C7 into the _time array
//Needs to be called before printing time or date
//We do not protect the GPx registers. They will be overwritten. The user has plenty of RAM if they need it.
bool RV3028::updateTime()
{
    if (readMultipleRegisters(RV3028_SECONDS, _time, TIME_ARRAY_LENGTH) == false)
        return(false); //Something went wrong

    if (is12Hour()) _time[TIME_HOURS] &= ~(1 << HOURS_AM_PM); //Remove this bit from value

    return true;
}

//Returns a pointer to array of chars that are the date in mm/dd/yyyy format because they're weird
char* RV3028::stringDateUSA()
{
    static char date[11]; //Max of mm/dd/yyyy with \0 terminator
    sprintf(date, "%02hhu/%02hhu/20%02hhu", BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_YEAR]));
    return(date);
}

//Returns a pointer to array of chars that are the date in dd/mm/yyyy format
char*  RV3028::stringDate()
{
    static char date[11]; //Max of dd/mm/yyyy with \0 terminator
    sprintf(date, "%02hhu/%02hhu/20%02hhu", BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_YEAR]));
    return(date);
}

//Returns a pointer to array of chars that represents the time in hh:mm:ss format
//Adds AM/PM if in 12 hour mode
char* RV3028::stringTime()
{
    static char time[11]; //Max of hh:mm:ssXM with \0 terminator

    if (is12Hour() == true)
    {
        char half = 'A';
        if (isPM()) half = 'P';

        sprintf(time, "%02hhu:%02hhu:%02hhu%cM", BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]), half);
    }
    else
        sprintf(time, "%02hhu:%02hhu:%02hhu", BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]));

    return(time);
}

char* RV3028::stringTimeStamp()
{
    static char timeStamp[25]; //Max of yyyy-mm-ddThh:mm:ss.ss with \0 terminator

    if (is12Hour() == true)
    {
        char half = 'A';
        if (isPM()) half = 'P';

        sprintf(timeStamp, "20%02hhu-%02hhu-%02hhu  %02hhu:%02hhu:%02hhu%cM", BCDtoDEC(_time[TIME_YEAR]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]), half);
    }
    else
        sprintf(timeStamp, "20%02hhu-%02hhu-%02hhu  %02hhu:%02hhu:%02hhu", BCDtoDEC(_time[TIME_YEAR]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]));

    return(timeStamp);
}

uint8_t RV3028::getSeconds()
{
    return BCDtoDEC(_time[TIME_SECONDS]);
}

uint8_t RV3028::getMinutes()
{
    return BCDtoDEC(_time[TIME_MINUTES]);
}

uint8_t RV3028::getHours()
{
    return BCDtoDEC(_time[TIME_HOURS]);
}

uint8_t RV3028::getWeekday()
{
    return BCDtoDEC(_time[TIME_WEEKDAY]);
}

uint8_t RV3028::getDate()
{
    return BCDtoDEC(_time[TIME_DATE]);
}

uint8_t RV3028::getMonth()
{
    return BCDtoDEC(_time[TIME_MONTH]);
}

uint16_t RV3028::getYear()
{
    return BCDtoDEC(_time[TIME_YEAR]) + 2000;
}

//Returns true if RTC has been configured for 12 hour mode
bool RV3028::is12Hour()
{
    uint8_t controlRegister2 = readRegister(RV3028_CTRL2);
    return(controlRegister2 & (1 << CTRL2_12_24));
}

//Returns true if RTC has PM bit set and 12Hour bit set
bool RV3028::isPM()
{
    uint8_t hourRegister = readRegister(RV3028_HOURS);
    if (is12Hour() && (hourRegister & (1 << HOURS_AM_PM)))
        return(true);
    return(false);
}

//Configure RTC to output 1-12 hours
//Converts any current hour setting to 12 hour
void RV3028::set12Hour()
{
    //Do we need to change anything?
    if (is12Hour() == false)
    {
        uint8_t hour = BCDtoDEC(readRegister(RV3028_HOURS)); //Get the current hour in the RTC

                                                             //Set the 12/24 hour bit
        uint8_t setting = readRegister(RV3028_CTRL2);
        setting |= (1 << CTRL2_12_24);
        writeRegister(RV3028_CTRL2, setting);

        //Take the current hours and convert to 12, complete with AM/PM bit
        bool pm = false;

        if (hour == 0)
            hour += 12;
        else if (hour == 12)
            pm = true;
        else if (hour > 12)
        {
            hour -= 12;
            pm = true;
        }

        hour = DECtoBCD(hour); //Convert to BCD

        if (pm == true) hour |= (1 << HOURS_AM_PM); //Set AM/PM bit if needed

        writeRegister(RV3028_HOURS, hour); //Record this to hours register
    }
}

//Configure RTC to output 0-23 hours
//Converts any current hour setting to 24 hour
void RV3028::set24Hour()
{
    //Do we need to change anything?
    if (is12Hour() == true)
    {
        //Not sure what changing the CTRL2 register will do to hour register so let's get a copy
        uint8_t hour = readRegister(RV3028_HOURS); //Get the current 12 hour formatted time in BCD
        bool pm = false;
        if (hour & (1 << HOURS_AM_PM)) //Is the AM/PM bit set?
        {
            pm = true;
            hour &= ~(1 << HOURS_AM_PM); //Clear the bit
        }

        //Change to 24 hour mode
        uint8_t setting = readRegister(RV3028_CTRL2);
        setting &= ~(1 << CTRL2_12_24); //Clear the 12/24 hr bit
        writeRegister(RV3028_CTRL2, setting);

        //Given a BCD hour in the 1-12 range, make it 24
        hour = BCDtoDEC(hour); //Convert core of register to DEC

        if (pm == true) hour += 12; //2PM becomes 14
        if (hour == 12) hour = 0; //12AM stays 12, but should really be 0
        if (hour == 24) hour = 12; //12PM becomes 24, but should really be 12

        hour = DECtoBCD(hour); //Convert to BCD

        writeRegister(RV3028_HOURS, hour); //Record this to hours register
    }
}

//ATTENTION: Real Time and UNIX Time are INDEPENDENT!
bool RV3028::setUNIX(uint32_t value)
{
    uint8_t unix_reg[4];
    unix_reg[0] = value;
    unix_reg[1] = value >> 8;
    unix_reg[2] = value >> 16;
    unix_reg[3] = value >> 24;

    return writeMultipleRegisters(RV3028_UNIX_TIME0, unix_reg, 4);
}

//ATTENTION: Real Time and UNIX Time are INDEPENDENT!
uint32_t RV3028::getUNIX()
{
    uint8_t unix_reg[4];
    readMultipleRegisters(RV3028_UNIX_TIME0, unix_reg, 4);
    return ((uint32_t)unix_reg[3] << 24) | ((uint32_t)unix_reg[2] << 16) | ((uint32_t)unix_reg[1] << 8) | unix_reg[0];
}

/*********************************
Set the alarm mode in the following way:
0: When minutes, hours and weekday/date match (once per weekday/date)
1: When hours and weekday/date match (once per weekday/date)
2: When minutes and weekday/date match (once per hour per weekday/date)
3: When weekday/date match (once per weekday/date)
4: When hours and minutes match (once per day)
5: When hours match (once per day)
6: When minutes match (once per hour)
7: All disabled � Default value
If you want to set a weekday alarm (setWeekdayAlarm_not_Date = true), set 'date_or_weekday' from 0 (Sunday) to 6 (Saturday)
********************************/
void RV3028::enableAlarmInterrupt(uint8_t min, uint8_t hour, uint8_t date_or_weekday, bool setWeekdayAlarm_not_Date, uint8_t mode, bool enable_clock_output)
{
    //disable Alarm Interrupt to prevent accidental interrupts during configuration
    disableAlarmInterrupt();
    clearAlarmInterruptFlag();

    //ENHANCEMENT: Add Alarm in 12 hour mode
    set24Hour();

    //Set WADA bit (Weekday/Date Alarm)
    if (setWeekdayAlarm_not_Date)
        clearBit(RV3028_CTRL1, CTRL1_WADA);
    else
        setBit(RV3028_CTRL1, CTRL1_WADA);

    //Write alarm settings in registers 0x07 to 0x09
    uint8_t alarmTime[3];
    alarmTime[0] = DECtoBCD(min);                //minutes
    alarmTime[1] = DECtoBCD(hour);                //hours
    alarmTime[2] = DECtoBCD(date_or_weekday);    //date or weekday
    //shift alarm enable bits
    if (mode > 0b111) mode = 0b111; //0 to 7 is valid
    if (mode & 0b001)
        alarmTime[0] |= 1 << MINUTESALM_AE_M;
    if (mode & 0b010)
        alarmTime[1] |= 1 << HOURSALM_AE_H;
    if (mode & 0b100)
        alarmTime[2] |= 1 << DATE_AE_WD;
    //Write registers
    writeMultipleRegisters(RV3028_MINUTES_ALM, alarmTime, 3);

    //enable Alarm Interrupt
    enableAlarmInterrupt();

    //Clock output?
    if (enable_clock_output)
        setBit(RV3028_INT_MASK, IMT_MASK_CAIE);
    else
        clearBit(RV3028_INT_MASK, IMT_MASK_CAIE);
}

void RV3028::enableAlarmInterrupt()
{
    setBit(RV3028_CTRL2, CTRL2_AIE);
}

//Only disables the interrupt (not the alarm flag)
void RV3028::disableAlarmInterrupt()
{
    clearBit(RV3028_CTRL2, CTRL2_AIE);
}

bool RV3028::readAlarmInterruptFlag()
{
    return readBit(RV3028_STATUS, STATUS_AF);
}

void RV3028::clearAlarmInterruptFlag()
{
    clearBit(RV3028_STATUS, STATUS_AF);
}

/*********************************
Countdown Timer Interrupt
********************************/
void RV3028::setTimer(bool timer_repeat, uint16_t timer_frequency, uint16_t timer_value, bool set_interrupt, bool start_timer, bool enable_clock_output)
{
    disableTimer();
    disableTimerInterrupt();
    clearTimerInterruptFlag();

    writeRegister(RV3028_TIMERVAL_0, timer_value & 0xff);
    writeRegister(RV3028_TIMERVAL_1, timer_value >> 8);

    uint8_t ctrl1_val = readRegister(RV3028_CTRL1);
    if (timer_repeat)
    {
        ctrl1_val |= 1 << CTRL1_TRPT;
    }
    else
    {
        ctrl1_val &= ~(1 << CTRL1_TRPT);
    }
    switch (timer_frequency)
    {
    case 4096:        // 4096Hz (default)        // up to 122us error on first time
        ctrl1_val &= ~3; // Clear both the bits
        break;

    case 64:        // 64Hz                    // up to 7.813ms error on first time
        ctrl1_val &= ~3; // Clear both the bits
        ctrl1_val |= 1;
        break;

    case 1:            // 1Hz                    // up to 7.813ms error on first time
        ctrl1_val &= ~3; // Clear both the bits
        ctrl1_val |= 2;
        break;

    case 60000:        // 1/60Hz                // up to 7.813ms error on first time
        ctrl1_val |= 3; // Set both bits
        break;

    }

    if (set_interrupt)
    {
        enableTimerInterrupt();
    }
    if (start_timer)
    {
        ctrl1_val |= (1 << CTRL1_TE);
    }
    writeRegister(RV3028_CTRL1, ctrl1_val);

    //Clock output?
    if (enable_clock_output)
        setBit(RV3028_INT_MASK, IMT_MASK_CTIE);
    else
        clearBit(RV3028_INT_MASK, IMT_MASK_CTIE);
}


void RV3028::enableTimerInterrupt()
{
    setBit(RV3028_CTRL2, CTRL2_TIE);
}

void RV3028::disableTimerInterrupt()
{
    clearBit(RV3028_CTRL2, CTRL2_TIE);
}

bool RV3028::readTimerInterruptFlag()
{
    return readBit(RV3028_STATUS, STATUS_TF);
}

void RV3028::clearTimerInterruptFlag()
{
    clearBit(RV3028_STATUS, STATUS_TF);
}

void RV3028::enableTimer()
{
    setBit(RV3028_CTRL1, CTRL1_TE);
}

void RV3028::disableTimer()
{
    clearBit(RV3028_CTRL1, CTRL1_TE);
}

/*********************************
Periodic Time Update Interrupt
********************************/
void RV3028::enablePeriodicUpdateInterrupt(bool every_second, bool enable_clock_output)
{
    disablePeriodicUpdateInterrupt();
    clearPeriodicUpdateInterruptFlag();

    if (every_second)
    {
        clearBit(RV3028_CTRL1, CTRL1_USEL);
    }
    else
    {    // every minute
        setBit(RV3028_CTRL1, CTRL1_USEL);
    }

    setBit(RV3028_CTRL2, CTRL2_UIE);

    //Clock output?
    if (enable_clock_output)
        setBit(RV3028_INT_MASK, IMT_MASK_CUIE);
    else
        clearBit(RV3028_INT_MASK, IMT_MASK_CUIE);
}

void RV3028::disablePeriodicUpdateInterrupt()
{
    clearBit(RV3028_CTRL2, CTRL2_UIE);
}

bool RV3028::readPeriodicUpdateInterruptFlag()
{
    return readBit(RV3028_STATUS, STATUS_UF);
}

void RV3028::clearPeriodicUpdateInterruptFlag()
{
    clearBit(RV3028_STATUS, STATUS_UF);
}

/*********************************
Enable the Trickle Charger and set the Trickle Charge series resistor (default is 15k)
TCR_3K  =  3kOhm
TCR_5K  =  5kOhm
TCR_9K  =  9kOhm
TCR_15K = 15kOhm
*********************************/
void RV3028::enableTrickleCharge(uint8_t tcr)
{
    if (tcr > 3) return;

    //Read EEPROM Backup Register (0x37)
    uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
    //Set TCR Bits (Trickle Charge Resistor)
    EEPROMBackup &= EEPROMBackup_TCR_CLEAR;        //Clear TCR Bits
    EEPROMBackup |= tcr << EEPROMBackup_TCR_SHIFT;    //Shift values into EEPROM Backup Register
    //Write 1 to TCE Bit
    EEPROMBackup |= 1 << EEPROMBackup_TCE_BIT;
    //Write EEPROM Backup Register
    writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup);
}

void RV3028::disableTrickleCharge()
{
    //Read EEPROM Backup Register (0x37)
    uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
    //Write 0 to TCE Bit
    EEPROMBackup &= ~(1 << EEPROMBackup_TCE_BIT);
    //Write EEPROM Backup Register
    writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup);
}


/*********************************
0 = Switchover disabled
1 = Direct Switching Mode
2 = Standby Mode
3 = Level Switching Mode
*********************************/
bool RV3028::setBackupSwitchoverMode(uint8_t val)
{
    if (val > 3)return false;
    bool success = true;

    //Read EEPROM Backup Register (0x37)
    uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
    if (EEPROMBackup == 0xFF) success = false;
    //Ensure FEDE Bit is set to 1
    EEPROMBackup |= 1 << EEPROMBackup_FEDE_BIT;
    //Set BSM Bits (Backup Switchover Mode)
    EEPROMBackup &= EEPROMBackup_BSM_CLEAR;        //Clear BSM Bits of EEPROM Backup Register
    EEPROMBackup |= val << EEPROMBackup_BSM_SHIFT;    //Shift values into EEPROM Backup Register
    //Write EEPROM Backup Register
    if (!writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup)) success = false;

    return success;
}


/*********************************
Clock Out functions
********************************/
void RV3028::enableClockOut(uint8_t freq)
{
    if (freq > 7) return; // check out of bounds
    //Read EEPROM CLKOUT Register (0x35)
    uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
    //Ensure CLKOE Bit is set to 1
    EEPROMClkout |= 1 << EEPROMClkout_CLKOE_BIT;
    //Shift values into EEPROM Backup Register
    EEPROMClkout |= freq << EEPROMClkout_FREQ_SHIFT;
    //Write EEPROM Backup Register
    writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);
}

void RV3028::enableInterruptControlledClockout(uint8_t freq)
{
    if (freq > 7) return; // check out of bounds
    //Read EEPROM CLKOUT Register (0x35)
    uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
    //Shift values into EEPROM Backup Register
    EEPROMClkout |= freq << EEPROMClkout_FREQ_SHIFT;
    //Write EEPROM Backup Register
    writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);

    //Set CLKIE Bit
    setBit(RV3028_CTRL2, CTRL2_CLKIE);
}

void RV3028::disableClockOut()
{
    //Read EEPROM CLKOUT Register (0x35)
    uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
    //Clear CLKOE Bit
    EEPROMClkout &= ~(1 << EEPROMClkout_CLKOE_BIT);
    //Write EEPROM CLKOUT Register
    writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);

    //Clear CLKIE Bit
    clearBit(RV3028_CTRL2, CTRL2_CLKIE);
}

bool RV3028::readClockOutputInterruptFlag()
{
    return readBit(RV3028_STATUS, STATUS_CLKF);
}

void RV3028::clearClockOutputInterruptFlag()
{
    clearBit(RV3028_STATUS, STATUS_CLKF);
}


//Returns the status byte
uint8_t RV3028::status(void)
{
    return(readRegister(RV3028_STATUS));
}

void RV3028::clearInterrupts() //Read the status register to clear the current interrupt flags
{
    writeRegister(RV3028_STATUS, 0);
}





/*********************************
FOR INTERNAL USE
********************************/
uint8_t RV3028::BCDtoDEC(uint8_t val)
{
    return ((val / 0x10) * 10) + (val % 0x10);
}

// BCDtoDEC -- convert decimal to binary-coded decimal (BCD)
uint8_t RV3028::DECtoBCD(uint8_t val)
{
    return ((val / 10) * 0x10) + (val % 10);
}

uint8_t RV3028::readRegister(uint8_t addr)
{
    _i2cPort->beginTransmission(RV3028_ADDR);
    _i2cPort->write(addr);
    _i2cPort->endTransmission();

    _i2cPort->requestFrom(RV3028_ADDR, (uint8_t)1);
    if (_i2cPort->available()) {
        return _i2cPort->read();
    }
    else {
        return (0xFF); //Error
    }
}

bool RV3028::writeRegister(uint8_t addr, uint8_t val)
{
    _i2cPort->beginTransmission(RV3028_ADDR);
    _i2cPort->write(addr);
    _i2cPort->write(val);
    if (_i2cPort->endTransmission() != 0)
        return (false); //Error: Sensor did not ack
    return(true);
}

bool RV3028::readMultipleRegisters(uint8_t addr, uint8_t * dest, uint8_t len)
{
    _i2cPort->beginTransmission(RV3028_ADDR);
    _i2cPort->write(addr);
    if (_i2cPort->endTransmission() != 0)
        return (false); //Error: Sensor did not ack

    _i2cPort->requestFrom(RV3028_ADDR, len);
    for (uint8_t i = 0; i < len; i++)
    {
        dest[i] = _i2cPort->read();
    }

    return(true);
}

bool RV3028::writeMultipleRegisters(uint8_t addr, uint8_t * values, uint8_t len)
{
    _i2cPort->beginTransmission(RV3028_ADDR);
    _i2cPort->write(addr);
    for (uint8_t i = 0; i < len; i++)
    {
        _i2cPort->write(values[i]);
    }

    if (_i2cPort->endTransmission() != 0)
        return (false); //Error: Sensor did not ack
    return(true);
}

bool RV3028::writeConfigEEPROM_RAMmirror(uint8_t eepromaddr, uint8_t val)
{
    bool success = waitforEEPROM();

    //Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
    uint8_t ctrl1 = readRegister(RV3028_CTRL1);
    ctrl1 |= 1 << CTRL1_EERD;
    if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
    //Write Configuration RAM Register
    writeRegister(eepromaddr, val);
    //Update EEPROM (All Configuration RAM -> EEPROM)
    writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
    writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_Update);
    if (!waitforEEPROM()) success = false;
    //Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
    ctrl1 = readRegister(RV3028_CTRL1);
    if (ctrl1 == 0x00)success = false;
    ctrl1 &= ~(1 << CTRL1_EERD);
    writeRegister(RV3028_CTRL1, ctrl1);
    if (!waitforEEPROM()) success = false;

    return success;
}

uint8_t RV3028::readConfigEEPROM_RAMmirror(uint8_t eepromaddr)
{
    bool success = waitforEEPROM();

    //Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
    uint8_t ctrl1 = readRegister(RV3028_CTRL1);
    ctrl1 |= 1 << CTRL1_EERD;
    if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
    //Read EEPROM Register
    writeRegister(RV3028_EEPROM_ADDR, eepromaddr);
    writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
    writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_ReadSingle);
    if (!waitforEEPROM()) success = false;
    uint8_t eepromdata = readRegister(RV3028_EEPROM_DATA);
    if (!waitforEEPROM()) success = false;
    //Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
    ctrl1 = readRegister(RV3028_CTRL1);
    if (ctrl1 == 0x00)success = false;
    ctrl1 &= ~(1 << CTRL1_EERD);
    writeRegister(RV3028_CTRL1, ctrl1);

    if (!success) return 0xFF;
    return eepromdata;
}

//True if success, false if timeout occured
bool RV3028::waitforEEPROM()
{
    unsigned long timeout = millis() + 500;
    while ((readRegister(RV3028_STATUS) & 1 << STATUS_EEBUSY) && millis() < timeout);

    return millis() < timeout;
}

void RV3028::reset()
{
    setBit(RV3028_CTRL2, CTRL2_RESET);
}


void RV3028::setBit(uint8_t reg_addr, uint8_t bit_num)
{
    uint8_t value = readRegister(reg_addr);
    value |= (1 << bit_num); //Set the bit
    writeRegister(reg_addr, value);
}

void RV3028::clearBit(uint8_t reg_addr, uint8_t bit_num)
{
    uint8_t value = readRegister(reg_addr);
    value &= ~(1 << bit_num); //Clear the bit
    writeRegister(reg_addr, value);
}

bool RV3028::readBit(uint8_t reg_addr, uint8_t bit_num)
{
    uint8_t value = readRegister(reg_addr);
    value &= (1 << bit_num);
    return value;

}
 
Hmm. At this point I am so lost and confused that I think one huge code is just simpler for my small brain.

I understand you can split the code into separate ino files, but I didn't even get that to compile correctly.
I basically don't understand how to format these extra ino files. You look it up on Google and it says simply list your code on separate ino files and the IDE will combine them. Not so, you obviously need to declare the required variables etc in each ino.

I think I should back out the door slowly....
You don't have many .ino files just one. You sprinkle your code in .h files splitting the code up into logical .h files by function.
I.e. have one for kbd routines perhaps and another for a particular data capture and perhaps another one for general subroutines. Whatever makes your code manageable and understandable.
 
Thanks. I will look through that. Thank you for taking the time to post examples.

The trouble is, after more years than I care to count, I have never understood the basics of C++ programming and managed to 'get by' using the Arduino IDE.

I have yet to find a tutorial that explains (to me) the methods and reasoning's behind how all this stuff operates and is set up.
It's fine me copy/pasting examples, but I need to try and understand the relationship between all these processes.

I literally copy/paste and cross fingers. If it doesn't work I try fiddling around until it does, rather than understand why it didn't work.

One of the issues is any tutorial I find tends to talk in language I just don't understand, as I think you have a knowledge of C++ already.
 
Yes the Software/Computer industry is populated by people who write books, examples etc for People Who know by people who know. No thought for those that don't know and are looking for knowledge.

I know the above sentence is not written entirely correctly but I am sure you know what I mean!

As far as your needs go just remember:

1) The main program is a .ino file and ONLY one should exist,
2) Variables and subroutines (code) can be put into .h files and imported (#included) into the main .ino file.

C++ has gotten too complex for it's own good, even Microsoft has moved away from it for it's main Windows development.
 
Thank you for the time spent. Yes, I know my sentences are a bit iffy!
With your help above, I have managed to sub-divided my code into 10 smaller .h files and it seems to work fine. I think I have FINALLY grasped the basics.
This will help make my code easier to maintain and read

Thanks for everyone's advice.

If anyone can recommend a book that explains this 'basic' stuff, then I think I should get one
 
Back
Top