//@微型气象站
//@光照度传感器(I2C)、温湿度传感器(One Wire)、地磁气压温度传感器(I2C)
//@锂电池保护模块、太阳能锂电池充电模块、时钟日历芯片、EEPROM储存芯片、无线口透传模块
//@MCU:ATmega328P
//@电压:锂电3.7V,可直接使用5V外接电源,但不可与锂电一同使用,太阳能充电输入电压4.35~6V

//@实测电流(锂电池):
//@MCU运行,未在测量,无线模块开启:31.9mA
//@MCU休眠,无线模块休眠,传感器闲置:450uA

////加载外部库//////////////////////////////////////////////

#include <Wire.h>    //I2C库(Arduino自带库)
#include <I2C.h>     //I2C库(Arduino自编库)

////测试用按钮、LED//////////////////////////////////////////////

#define Test_Button_Pin 3  //测试按钮针脚位
#define Test_LED_Pin    15  //测试LED针脚位

////电压//////////////////////////////////////////////

#define Voltage_Pin 0  //电压值读取针脚位,A0
#define Voltage_Read() (97.88*5*1.1*analogRead(Voltage_Pin)/1023)  //读取电压值并计算终值,100倍于原值,选择片内1.1V基准电压源
//分压电阻精度误差调整系数*分压电阻比例(75K+300K,最高检测电压5.5V)*标准电压值*检测读数/1023 + 输入二极管压降(实测0.224-0.336V)
unsigned int Voltage_Result = 0;  //电压值,100倍于原值

////BH1751FVI光照度芯片//////////////////////////////////////////////

#define I2C_Address_BH1751FVI 0x23    //BH1751FVI芯片I2C地址转换为7位地址
unsigned char BH1751FVI_Mode = 0x11;  //测量模式
/*
0x10 连续高分辨率模式
在1lx 分辨率下开始测量,测量时间一般为120ms,最大180ms
0x11 连续高分辨率模式2
在0.5lx 分辨率下开始测量,测量时间一般为120ms
0x13 连续低分辨率模式
在41lx 分辨率下开始测量,测量时间一般为16ms,最大24ms
0x20 一次高分辨率模式
在1lx 分辨率下开始测量,测量时间一般为120ms,测量后自动设置为断电模式
0x21 一次高分辨率模式2
在0.5lx 分辨率下开始测量,测量时间一般为120ms,测量后自动设置为断电模式
0x23 一次低分辨率模式
在41lx 分辨率下开始测量,测量时间一般为16ms,测量后自动设置为断电模式
*/
unsigned int BH1751FVI_Result = 0;    //测量结果

////BMP085气压计//////////////////////////////////////////////

#define I2C_Address_BMP085 0x77  //BMP085气压计芯片I2C地址
const unsigned char REG_BMP085_Command       = 0xF4;  //测量控制寄存器地址
const unsigned char Param_BMP085_Close       = 0x00;  //将芯片设置到断电状态
const unsigned char Param_BMP085_Open        = 0x01;  //将芯片设置到通电状态,等待测量指令
// const unsigned char Param_BMP085_Rest        = 0x01;  //重置数字寄存器
const unsigned char Param_BMP085_Temperature = 0x2E;  //温度,4.5ms
// const unsigned char Param_BMP085_Pressure0   = 0x34;  //低精度气压,4.5ms
// const unsigned char Param_BMP085_Pressure1   = 0x74;  //中精度气压,7.5ms
// const unsigned char Param_BMP085_Pressure2   = 0xB4;  //高精度气压,13.5ms
// const unsigned char Param_BMP085_Pressure3   = 0xF4;  //最高精度气压,25.5ms
const unsigned char REG_BMP085_MSB           = 0xF6;  //测量值寄存器高位
// const unsigned char REG_BMP085_LSB           = 0xF7;  //测量值寄存器低位
// const unsigned char REG_BMP085_XLSB          = 0xF8;  //测量值寄存器低位

#define OSS 3  //气压采样精度设置,低精度到高精度 0~3

//校准值
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
//b5用于计算BMP085_read_Temperature(),它也同时用于BMP085_read_Pressure()
//所以BMP085_read_Temperature()必须放在BMP085_read_Pressure()之前
long b5;

int BMP085_Temperature = 0;  //温度值,10倍于原值
unsigned long BMP085_Pressure = 0;  //气压值(Pa)

////HMC5883L数字罗盘//////////////////////////////////////////////

#define I2C_Address_HMC5883L 0x1E  //HMC5883L数字罗盘芯片I2C地址
const unsigned char REG_HMC5883L_SetA   = 0x00;  //配置寄存器A地址
const unsigned char REG_HMC5883L_SetB   = 0x01;  //配置寄存器B地址
const unsigned char REG_HMC5883L_Mode   = 0x02;  //模式寄存器地址
const unsigned char REG_HMC5883L_X      = 0x03;  //X轴数据寄存器MSB地址
// const unsigned char REG_HMC5883L_Z      = 0x05;  //Z轴数据寄存器MSB地址
// const unsigned char REG_HMC5883L_Y      = 0x07;  //Y轴数据寄存器MSB地址
// const unsigned char REG_HMC5883L_Status = 0x09;  //状态寄存器地址
// const unsigned char REG_HMC5883L_IDA    = 0x10;  //识别寄存器A地址
// const unsigned char REG_HMC5883L_IDB    = 0x11;  //识别寄存器B地址
// const unsigned char REG_HMC5883L_IDC    = 0x12;  //识别寄存器C地址

const int HMC5883L_Declination = -4;  //磁偏角修正值

unsigned int HMC5883L_Result = 0;  //测量结果

////AM2302温湿度计//////////////////////////////////////////////

#define AM2302_Pin 9  //针脚位
unsigned int AM2302_Humidity = 0;  //湿度,10倍于原值
int AM2302_Temperature = 0;  //温度,10倍于原值

////EEPROM储存芯片//////////////////////////////////////////////

#define I2C_Address_EEPROM 0x53  //此处为AT24C256的地址

unsigned int        DATA_EEPROM_TotalKB    = 32;    //芯片总容量,AT24C256 - 32K
unsigned int        DATA_EEPROM_DataUsed   = 0;     //已使用数据个数
const unsigned char DATA_EEPROM_DataLength = 24;    //每个数据长度,24字节
const unsigned int  DATA_EEPROM_DataFirst  = 0x20;  //首个数据地址

const unsigned int  REG_EEPROM_TotalKB     = 0x00;  //EEPROM芯片总容量(Kbyte),2字节
// const unsigned int  REG_EEPROM_DataLength  = 0x02;  //每个数据长度(Byte),1字节
const unsigned int  REG_EEPROM_DataUsed    = 0x03;  //已使用数据个数,2字节
// const unsigned int  REG_EEPROM_DataFirst   = 0x05;  //首个数据地址,2字节
const unsigned int  REG_EEPROM_LastPowerOn = 0x07;  //最后开机时间,6字节

////PCF8563时钟芯片//////////////////////////////////////////////

#include <Rtc_Pcf8563.h>  //PCF8563库
Rtc_Pcf8563 PCF8563;
#define PCF8563_Pin 2  //PCF8563时钟芯片报警中断脚

// #define I2C_Address_PCF8563  0x51  //PCF8563时钟芯片I2C地址
// #define REG_PCF8563_Year   0x08  //年
// #define REG_PCF8563_Month  0x07  //月
// #define REG_PCF8563_Week   0x06  //周
// #define REG_PCF8563_Day    0x05  //日
// #define REG_PCF8563_Hour   0x04  //时
// #define REG_PCF8563_Minute 0x03  //分
// #define REG_PCF8563_Second 0x02  //秒
// #define REG_PCF8563_Alarm_Week   0x0C  //报警-周
// #define REG_PCF8563_Alarm_Day    0x0B  //报警-日
// #define REG_PCF8563_Alarm_Hour   0x0A  //报警-时
// #define REG_PCF8563_Alarm_Minute 0x09  //报警-分
// #define REG_PCF8563_Control_1    0x00  //控制状态1
// #define REG_PCF8563_Control_2    0x01  //控制状态2
// #define REG_PCF8563_Frequency    0x0D  //频率寄存器
// #define REG_PCF8563_Timer_1      0x0E  //定时器1寄存器
// #define REG_PCF8563_Timer_2      0x0F  //定时器倒计数寄存器

// unsigned char Time_year, Time_month, Time_day, Time_hour, Time_minute, Time_sec;

////模块电源MOS开关//////////////////////////////////////////////

#define PowerSwitch_Pin_EEPROM 8  //EEPROM电源开关 针脚位
#define PowerSwitch_Pin_Sensor 7  //传感器电源开关 针脚位
#define PowerSwitch_Pin_Radio  6  //无线模块电源开关 针脚位

#define Open_EEPROM()  {digitalWrite(PowerSwitch_Pin_EEPROM,HIGH);delay(10);}    //开EEPROM电源开关
#define Close_EEPROM() {delay(10);digitalWrite(PowerSwitch_Pin_EEPROM,LOW);}     //关EEPROM电源开关
#define Open_Sensor()  {digitalWrite(PowerSwitch_Pin_Sensor,HIGH);delay(1000);}  //开传感器电源开关
#define Close_Sensor() {digitalWrite(PowerSwitch_Pin_Sensor,LOW);}               //关传感器电源开关
#define Open_Radio()   {digitalWrite(PowerSwitch_Pin_Radio,HIGH);delay(2000);}   //开无线数传模块电源开关
#define Close_Radio()  {if(!PowerSwitch_Radio_Enable){delay(1000);digitalWrite(PowerSwitch_Pin_Radio,LOW);}}    //关无线数传模块电源开关

bool NoSleep = 0;  //是否强制不进入休眠模式
bool PowerSwitch_Radio_Enable = 0;  //是否强制无线模块电源一直开着

//串口部分////////////////////////////////////////////////

String comdata = "";  //串口读取数据值
unsigned int CommandVal = 0;   //串口命令值,0表示没有命令

//串口命令值
#define Command_EEPROM_AllAndEccAndClear       0x01  //传输所有数据(带CRC校验)
#define Command_EEPROM_AllNoEccAndClear        0x02  //无确认传输所有数据
#define Command_EEPROM_AllNoEccNoClear         0x03  //无确认传输所有数据,不清除EEPROM数据
#define Command_EEPROM_AllNoEccNoClearFormat   0x04  //无确认传输所有数据,不清除EEPROM数据,格式化回传数据
#define Command_EEPROM_LastNoEccNoClearFormat  0x05  //无确认传输最后一个数据,不清除EEPROM数据,格式化回传数据
#define Command_ReadImmediately                0x06  //读当前数据并传输
#define Command_CurrentTime                    0x07  //传输当前时间
#define Command_SetTime                        0x08  //设置当前时间
// #define Command_EEPROM_LastPowerOn             0x09  //读最后开机时间
#define Command_EEPROM_Information             0x0A  //传输当前EEPROM信息
#define Command_EEPROM_TotalKB                 0x0B  //设置EEPROM芯片总容量(KByte)
#define Command_EEPROM_Clear                   0x0C  //清空EEPROM中所有数据(将已使用数据个数写0)
#define Command_PowerSwitch_Radio_OPEN         0x0D  //强制开启无线模块电源
#define Command_NoSleep                        0x0E  //强制不进入休眠模式
#define Command_EEPROM_WritePresent            0x0F  //将当前数据写入EEPROM

//////////////////////////////////////////////////

volatile bool IntTime = 0;    //判断是否产生时间中断
volatile bool IntButton = 0;  //判断是否按下按钮

//外部中断0////////////////////////////////////////////////
void Int_time()  //时钟唤醒中断
{
  SMCR &= ~(1<<SE);    //停止休眠
  detachInterrupt(0);  //禁止再次接收时钟中断
  IntTime = 1;         //告知程序已经在时钟唤醒中断内
}
//外部中断1////////////////////////////////////////////////
void Int_button()  //按钮中断
{
  SMCR &= ~(1<<SE);    //停止休眠
  detachInterrupt(1);  //禁止再次接收时钟中断
  IntButton = 1;       //按钮按下
}

void setup()
{
  //时钟中断、测试按钮、LED 针脚位设置
  pinMode(PCF8563_Pin, INPUT);      //时钟芯片中断 针脚位
  pinMode(Test_Button_Pin, INPUT);  //测试用按钮   针脚位
  pinMode(Test_LED_Pin, OUTPUT);    //测试用灯     针脚位
  //电源开关 针脚位设置
  pinMode(PowerSwitch_Pin_EEPROM, OUTPUT);  //EEPROM芯片 电源开关
  pinMode(PowerSwitch_Pin_Sensor, OUTPUT);  //传感器     电源开关
  pinMode(PowerSwitch_Pin_Radio, OUTPUT);   //无线模块   电源开关
  //电压检测设置
  analogReference(INTERNAL);  //选择片内1.1V基准电压源
  //MCU休眠设置
  SMCR = 0x04;                //休眠模式 - 掉电模式

  Serial.begin(9600);  //串口波特率设置
  Wire.begin();        //开启I2C总线
  Open_Radio();        //打开无线模块

  //时钟芯片首次定时唤醒时间设置
  PCF8563.getDate();  //获取当前日期
  PCF8563.getTime();  //获取当前时间
  PCF8563.setAlarm((PCF8563.getMinute()<30?30:0), 99, 99, 99);  //分、时、日、周,设置为99时此位无效

  //写入EEPROM首数据
  // EEP.write(I2C_Address_EEPROM, REG_EEPROM_TotalKB,    DATA_EEPROM_TotalKB);     //EEPROM芯片总容量(Kbyte)
  // EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataLength, DATA_EEPROM_DataLength);  //每个数据长度(Byte)
  // EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataFirst,  DATA_EEPROM_DataFirst);   //首个数据地址
  // delay(10);  //EEPROM读写转换周期
  //读出现在芯片的已使用数据个数
  DATA_EEPROM_DataUsed = I2C.read(I2C_Address_EEPROM, REG_EEPROM_DataUsed, 2);  //已使用数据个数
  delay(10);  //EEPROM读写转换周期
  //EEPROM写最后开机时间
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn,   (unsigned char)PCF8563.getYear());    //年
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+1, (unsigned char)PCF8563.getMonth());   //月
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+2, (unsigned char)PCF8563.getDay());     //日
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+3, (unsigned char)PCF8563.getHour());    //时
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+4, (unsigned char)PCF8563.getMinute());  //分
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+5, (unsigned char)PCF8563.getSecond());  //秒

  //开机校准方向
  Test_init();
  delay(1000);

  //外部中断
  attachInterrupt(0, Int_time, FALLING);  //时钟芯片中断
  attachInterrupt(1, Int_button, LOW);    //按钮中断(此设置一定要在开机校准方向后
}

void loop()
{
  unsigned long _time = millis();  //用于超时

  while(1)
  {
    //串口读取
    if(Serial.available() > 0)
    {
      while(Serial.available() > 0) //当串口有信息传入,读取之
      {
        comdata += char(Serial.read());
        delay(2);
      }
      if(comdata.length() > 0)
      {
        //串口命令格式:头字($*)+ 命令数(2位数字);例:$*01
        if(comdata.substring(0,2) == "$*") CommandVal = (comdata.substring(2,4)).toInt();  //命令头字
        comdata = ""; //清空变量
      }
      if(CommandVal)  //如果有命令
      {
        Wireless(CommandVal);
        CommandVal = 0;  //清空命令值
      }

      _time = millis();  //重新开始超时循环
    }

    if(IntTime)  //时钟中断发生
    {
      Sensor_Read();   //读取传感器数据
      Open_Radio();    //打开无线模块电源
      EEPROM_Write();  //把数据存入EEPROM

      //设置时钟芯片下次定时时间
      PCF8563.setAlarm((PCF8563.getMinute()<30?30:0), 99, 99, 99);  //设置时钟芯片唤醒时间,分、时、日、周,设置为99时此位无效

      IntTime = 0;                            //告知程序已经出了时钟唤醒中断
      attachInterrupt(0, Int_time, FALLING);  //重新打开时钟芯片中断

      _time = millis();  //重新开始超时循环
    }

    if(IntButton)  //按钮被按下
    {
      Open_Radio();  //打开无线模块电源

      if(!NoSleep)  //进入无休眠、无线模块强制打开状态
      {
        NoSleep = 1;                   //打开强制无休眠模式
        PowerSwitch_Radio_Enable = 1;  //打开强制开启无线模块模式

        Serial.println("No sleep.");
        Serial.println();

        digitalWrite(Test_LED_Pin, 1);  //打开测试灯
        delay(50);  //延时去毛刺,同时用于闪灯
        digitalWrite(Test_LED_Pin, 0);  //关闭测试灯
        delay(100);
        digitalWrite(Test_LED_Pin, 1);  //打开测试灯
        delay(50);  //延时去毛刺,同时用于闪灯
        digitalWrite(Test_LED_Pin, 0);  //关闭测试灯
      }
      else  //进入正常状态
      {
        NoSleep = 0;                   //关闭强制无休眠模式
        PowerSwitch_Radio_Enable = 0;  //关闭强制开启无线模块模式

        Serial.println("Sleep mode.");
        Serial.println();

        digitalWrite(Test_LED_Pin, 1);  //打开测试灯
        delay(100);  //延时去毛刺,同时用于闪灯
        digitalWrite(Test_LED_Pin, 0);  //关闭测试灯
        delay(500);
      }

      IntButton = 0;  //按钮复位
      attachInterrupt(1, Int_button, FALLING); //再次打开中断

      _time = millis();  //重新开始超时循环
    }

    if((millis() - _time) > 30000) break;  //超时设置,30s内无串口命令则跳出循环进入休眠模式
  }

  //启动MCU休眠模式,直到被外部中断唤醒(时钟芯片或串口读取到数据)
  if(!NoSleep)
  {
    Serial.print(PCF8563.formatDate(2));
    Serial.print(' ');
    Serial.println(PCF8563.formatTime());
    Serial.println("Sleep.");
    Serial.println();

    PowerSwitch_Radio_Enable = 0;    //关闭强制开启无线模块模式
    Close_Radio();                   //关闭无线模块
    SMCR |= (1<<SE);                 //开启休眠模式
    __asm__ __volatile__ ("SLEEP");  //进入休眠模式
  }
}

////串口输入模块//////////////////////////////////////////////

String Wait_Input()
{
  bool _Judge = 0;  //判断是否完成输入
  String _comdata = "";  //串口输入临时变量

  while(_Judge == 0)  //判断输入完成后退出循环
  {
    while(Serial.available() > 0)  //等待串口输入
    {
      _comdata += char(Serial.read());
      delay(2);
    }
    if(_comdata.length() > 0) _Judge = 1;  //将判断字设为true退出循环
  }

  return _comdata;
}

//串口输出当前数据
void Print_CurrentData()
{
  //注意:需确保串口无线数传模块已经开启
  Serial.print(PCF8563.formatDate(2));
  Serial.print(' ');
  Serial.println(PCF8563.formatTime());
  Serial.print("Vol: ");
  Serial.print(float(Voltage_Result)/100);
  Serial.println(" V");
  Serial.print("Tem: ");
  Serial.print(float(AM2302_Temperature)/10);
  Serial.println(" ^C");
  Serial.print("Hum: ");
  Serial.print(float(AM2302_Humidity)/10);
  Serial.println(" %RH");
  Serial.print("Pre: ");
  Serial.print(BMP085_Pressure);
  Serial.println(" Pa");
  Serial.print("Lig: ");
  Serial.print(BH1751FVI_Result);
  Serial.println(" lx");
  Serial.print("Geo: ");
  Serial.print(HMC5883L_Result);
  Serial.println(" ^");
  Serial.println();
}

////开机校准方向//////////////////////////////////////////////

void Test_init()
{
  unsigned long _time = millis();  //用于超时

  Serial.print(PCF8563.formatDate(2));
  Serial.print(' ');
  Serial.println(PCF8563.formatTime());
  Serial.println("Set dir.");
  Serial.println();

  while(1)
  {
    HMC5883L_init();  //配置HMC5883L
    HMC5883L_Read();  //读取地磁方向

    if(HMC5883L_Result%90 == 0) digitalWrite(Test_LED_Pin, 0);  //如果地磁在4个90度方向,关闭测试LED
    else digitalWrite(Test_LED_Pin, 1);  //否则打开测试LED

    if(!digitalRead(Test_Button_Pin))  //按下测试按钮后退出
    {
      digitalWrite(Test_LED_Pin, 0);  //关闭测试灯

      Serial.println("Dir set OK.");
      Serial.println();

      return;
    }

    if(millis() - _time > 30000)  //如果操作时间大于30s,超时退出
    {
      digitalWrite(Test_LED_Pin, 0);

      Serial.println("Dir set OT.");
      Serial.println();

      return;
    }
  }
}

////读取传感器//////////////////////////////////////////////

void Sensor_Read()
{
  Open_Sensor();  //打开传感器电源
  delay(2000);

  HMC5883L_init();  //配置HMC5883L
  BMP085_init();    //读取BMP085的校准值

  PCF8563.getDate();  //获取当前日期
  PCF8563.getTime();  //获取当前时间

  while(AM2302_Read() == 0);        //温、湿度值
  BMP085_read_Temperature();        //温度(读大气压之前必须读此温度值,作为大气压校准)
  BMP085_read_Pressure();           //大气压
  BH1751FVI_Read();                 //光照度
  HMC5883L_Read();                  //地磁方向
  Voltage_Result = Voltage_Read();  //电压

  Close_Sensor();  //关闭传感器电源
}

////数据写入EEPROM//////////////////////////////////////////////

void EEPROM_Write()
{
  Open_EEPROM();  //打开EEPROM电源
  delay(10);

  //写入地址=数据首地址 + 每个数据长度 * 当前已使用数据个数
  DATA_EEPROM_DataUsed = I2C.read(I2C_Address_EEPROM, REG_EEPROM_DataUsed, 2);  //再次核对已使用数据个数
  delay(10);  //EEPROM读写周期
  unsigned int writeAdd = DATA_EEPROM_DataFirst + DATA_EEPROM_DataLength * DATA_EEPROM_DataUsed;

  //写入EEPROM
  EEP.write(I2C_Address_EEPROM, writeAdd,    (unsigned char)PCF8563.getYear());    //年
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+1,  (unsigned char)PCF8563.getMonth());   //月
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+2,  (unsigned char)PCF8563.getDay());     //日
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+3,  (unsigned char)PCF8563.getHour());    //时
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+4,  (unsigned char)PCF8563.getMinute());  //分
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+5,  Voltage_Result);      //电压
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+7,  AM2302_Temperature);  //温度
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+9,  AM2302_Humidity);     //湿度
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+11, BMP085_Pressure);     //大气压
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+15, BH1751FVI_Result);    //光照度
  delay(10);  //EEPROM写周期
  EEP.write(I2C_Address_EEPROM, writeAdd+17, HMC5883L_Result);     //地磁方向
  delay(10);  //EEPROM写周期

  DATA_EEPROM_DataUsed++;  //已使用数据个数增一位
  EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataUsed, DATA_EEPROM_DataUsed);  //已使用数据个数写入EEPROM
  delay(10);  //EEPROM读写周期

  //校验EEPROM写入数据(未写)

  Close_EEPROM();  //关闭EEPROM电源

  Print_CurrentData();  //串口输出当前数据
  // Serial.print(PCF8563.formatDate(2)); Serial.print(' '); Serial.println(PCF8563.formatTime());
  // Serial.println("EEPROM write OK.");
  // Serial.println();
}

////读取EEPROM数据//////////////////////////////////////////////

void EEPROM_Read(unsigned int num, bool crc, bool clear, bool format)
{
  //如果EEPROM中没有数据
  if(DATA_EEPROM_DataUsed == 0)
  {
    Serial.println("EEPROM no data.");  //传输出错信息
    Serial.println();
    return;
  }

  String _date, _time;

  //读取地址 = 数据首地址 + 每个数据长度 * (需要读取的位数 - 1)
  unsigned int ReadAdd = DATA_EEPROM_DataFirst + DATA_EEPROM_DataLength * (num - 1);

  //读EEPROM内容
  _date = (String)I2C.read(I2C_Address_EEPROM, ReadAdd, 1);                  //年
  _date = _date + '-' + (String)I2C.read(I2C_Address_EEPROM, ReadAdd+1, 1);  //月
  _date = _date + '-' + (String)I2C.read(I2C_Address_EEPROM, ReadAdd+2, 1);  //日
  _time = (String)I2C.read(I2C_Address_EEPROM, ReadAdd+3, 1);                //时
  _time = _time + ':' + (String)I2C.read(I2C_Address_EEPROM, ReadAdd+4, 1);  //分
  Voltage_Result     = I2C.read(I2C_Address_EEPROM, ReadAdd+5,  2);          //电压
  AM2302_Temperature = (int)I2C.read(I2C_Address_EEPROM, ReadAdd+7, 2);      //温度(需转换为int类型)
  AM2302_Humidity    = I2C.read(I2C_Address_EEPROM, ReadAdd+9,  2);          //湿度
  BMP085_Pressure    = I2C.read(I2C_Address_EEPROM, ReadAdd+11, 4);          //大气压
  BH1751FVI_Result   = I2C.read(I2C_Address_EEPROM, ReadAdd+15, 2);          //光照度
  HMC5883L_Result    = I2C.read(I2C_Address_EEPROM, ReadAdd+17, 2);          //地磁方向

  //已使用数据个数减一位
  if(clear)
  {
    delay(10);  //EEPROM读写转换周期
    DATA_EEPROM_DataUsed--;
    I2C.write(I2C_Address_EEPROM, REG_EEPROM_DataUsed, DATA_EEPROM_DataUsed);  //写入EEPROM
    delay(10);  //EEPROM写周期
  }

  //无线发送数据
  if(format)  //发送格式化数据
  {
    Serial.print(_date);
    Serial.print(' ');
    Serial.println(_time);
    Serial.print("Vol: ");
    Serial.print(float(Voltage_Result)/100);
    Serial.println(" V");
    Serial.print("Tem: ");
    Serial.print(float(AM2302_Temperature)/10);
    Serial.println(" ^C");
    Serial.print("Hum: ");
    Serial.print(float(AM2302_Humidity)/10);
    Serial.println(" %RH");
    Serial.print("Pre: ");
    Serial.print(BMP085_Pressure);
    Serial.println(" Pa");
    Serial.print("Lig: ");
    Serial.print(BH1751FVI_Result);
    Serial.println(" lx");
    Serial.print("Geo: ");
    Serial.print(HMC5883L_Result);
    Serial.println(" ^");
  }
  else  //发送打包数据
  {
    Serial.print(_date);
    Serial.print(',');
    Serial.print(_time);
    Serial.print(',');
    Serial.print(float(Voltage_Result)/100);
    Serial.print(',');
    Serial.print(float(AM2302_Temperature)/10);
    Serial.print(',');
    Serial.print(float(AM2302_Humidity)/10);
    Serial.print(',');
    Serial.print(BMP085_Pressure);
    Serial.print(',');
    Serial.print(BH1751FVI_Result);
    Serial.print(',');
    Serial.println(HMC5883L_Result);

    //加CRC校验值
    // if(crc)
    // {
    // unsigned char _CRC = 0;
    // for(unsigned char i = 0; i < SendString.length(); i++) _CRC |= SendString.charAt(i);
    // SendString += (String)_CRC;
    // }
  }
  Serial.println();
}

////无线传输数据//////////////////////////////////////////////
void Wireless(unsigned char command)
{
  //从最后一个数据开始回传,每传完一个数据就把EEPROM中的此位数据清除掉(仅减少一位数据位)
  //命令:
  // 1 - 传输所有数据(带CRC校验)
  // 2 - 无确认传输所有数据
  // 3 - 无确认传输所有数据,不清除EEPROM数据
  // 4 - 无确认传输所有数据,不清除EEPROM数据,格式化回传数据
  // 5 - 无确认传输最后一个数据,不清除EEPROM数据,格式化回传数据
  // 6 - 读当前数据并传输
  // 7 - 传输当前时间
  // 8 - 设置当前时间
  // 9 - 读最后开机时间 - 未用
  // 10 - 传输当前EEPROM信息
  // 11 - 设置EEPROM芯片总容量(KByte)
  // 12 - 清空EEPROM中所有数据(将已使用数据个数写0)
  // 13 - 强制开启无线模块电源
  // 14 - 强制不进入休眠模式
  // 15 - 将当前数据写入EEPROM

  Open_EEPROM();  //打开EEPROM电源

  if(command == Command_EEPROM_AllAndEccAndClear)  //传输所有数据(带CRC校验),每传完一个数据要求回传确认信息
  {
    for(unsigned int i = DATA_EEPROM_DataUsed; i >=1 ; i--)  //循环读取、发送
    {
      EEPROM_Read(i, 1, 1, 0);

      //等待回传
      unsigned long _time = millis();
      String comval = "";
      while(1)
      {
        while(Serial.available() > 0) //当串口有信息传入,读取之
        {
          comval += char(Serial.read());
          delay(2);
        }
        if(comval == "Next") break;
        else if (comval == "Again")
        {
          DATA_EEPROM_DataUsed++;  //已使用数据个数重新增一位
          EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataUsed, DATA_EEPROM_DataUsed);  //写入EEPROM
          delay(10);  //EEPROM写周期
          i++;
          break;
        }
        if ((millis() - _time) > 2000)  //超时2s直接结束
        {
          DATA_EEPROM_DataUsed++;  //已使用数据个数重新增一位
          EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataUsed, DATA_EEPROM_DataUsed);  //写入EEPROM
          delay(10);  //EEPROM写周期
          i++;
          Close_EEPROM();  //关闭EEPROM电源
          return;
        }
      }
    }
  }
  else if(command == Command_EEPROM_AllNoEccAndClear)  //无确认传输所有数据
  {
    for(unsigned int i = DATA_EEPROM_DataUsed; i >=1 ; i--)
    {
      EEPROM_Read(i, 0, 1, 0);
      delay(100);  //间隔时间
    }
  }
  else if(command == Command_EEPROM_AllNoEccNoClear)  //无确认传输所有数据,不清除EEPROM数据
  {
    for(unsigned int i = DATA_EEPROM_DataUsed; i >=1 ; i--)
    {
      EEPROM_Read(i, 0, 0, 0);
      delay(100);  //间隔时间
    }
  }
  else if(command == Command_EEPROM_AllNoEccNoClearFormat)  //无确认传输所有数据,不清除EEPROM数据,格式化回传数据
  {
    for(unsigned int i = DATA_EEPROM_DataUsed; i >=1 ; i--)
    {
      EEPROM_Read(i, 0, 0, 1);
      delay(100);  //间隔时间
    }
  }
  else if(command == Command_EEPROM_LastNoEccNoClearFormat)  //无确认传输最后一个数据,不清除EEPROM数据
  {
    EEPROM_Read(DATA_EEPROM_DataUsed, 0, 0, 1);
  }
  else if(command == Command_ReadImmediately)  //如果是要求传输当前传感器数据
  {
    Serial.println("Reading Sensor...");
    Serial.println();

    Sensor_Read();  //读取当前数据

    Print_CurrentData();  //串口输出当前数据
  }
  else if(command == Command_CurrentTime)  //如果是要求传输当前时间
  {
    Serial.print(PCF8563.formatDate(2));
    Serial.print(' ');
    Serial.println(PCF8563.formatTime());
    Serial.println();
  }
  else if(command == Command_SetTime)  //如果是要求设置当前时间,输入格式为:“年月日周时分秒”,连续14个数字,如13011603234500
  {
    Serial.println("Input time.");  //输入提示符
    String comdata = Wait_Input();  //等待串口输入

    //设置时钟芯片时间值
    PCF8563.setDate((comdata.substring(4,6)).toInt(), (comdata.substring(6,8)).toInt(), (comdata.substring(2,4)).toInt(), 0, (comdata.substring(0,2)).toInt());  //设置日期:日、周、月、世纪、年
    PCF8563.setTime(comdata.substring(8,10).toInt(), comdata.substring(10,12).toInt(), comdata.substring(12,14).toInt());  //设置时间:时、分、秒

    Serial.print(PCF8563.formatDate(2));
    Serial.print(' ');
    Serial.println(PCF8563.formatTime());
    Serial.println("Time set OK.");
    Serial.println();
  }
  // else if(command == Command_EEPROM_LastPowerOn)  //读取最后开机时间(会死机)
  // {
  // String _date, _time;

  // _date = (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn, 1);                  //年
  // _date = _date + '-' + (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+1, 1);  //月
  // _date = _date + '-' + (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+2, 1);  //日
  // _time = (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+3, 1);                //时
  // _time = _time + ':' + (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+4, 1);  //分
  // _time = _time + ':' + (String)I2C.read(I2C_Address_EEPROM, REG_EEPROM_LastPowerOn+5, 1);  //秒

  // Serial.print("Last poweron : ");
  // Serial.print(_date); Serial.print(' '); Serial.println(_time);
  // Serial.println();
  // }
  else if(command == Command_EEPROM_Information)  //传输当前EEPROM信息
  {
    Serial.print("EEPROM: ");
    Serial.print(DATA_EEPROM_TotalKB = I2C.read(I2C_Address_EEPROM, REG_EEPROM_TotalKB, 2));
    Serial.println(" Kbyte");  //EEPROM芯片总容量(Kbyte),同时更新变量数据
    Serial.print("Data used: ");
    Serial.println(DATA_EEPROM_DataUsed = I2C.read(I2C_Address_EEPROM, REG_EEPROM_DataUsed, 2));  //已使用数据个数,同时更新变量数据
    Serial.print("Data remain: ");
    Serial.println((DATA_EEPROM_TotalKB*1024 - DATA_EEPROM_DataFirst - DATA_EEPROM_DataUsed * DATA_EEPROM_DataLength) /DATA_EEPROM_DataLength);  //剩余可使用数据个数=(芯片总容量-首个数据地址-已使用数据个数*每个数据长度)/每个数据长度
    Serial.println();
  }
  else if(command == Command_EEPROM_TotalKB)  //设置EEPROM总容量(KByte)
  {
    Serial.println("Input EEPROM size (KByte).");  //输入提示符
    String comdata = Wait_Input();  //等待串口输入

    EEP.write(I2C_Address_EEPROM, REG_EEPROM_TotalKB, (unsigned int)comdata.toInt());
    delay(10);  //EEPROM读写转换周期
    Serial.print("EEPROM size set to ");
    Serial.print(DATA_EEPROM_TotalKB = I2C.read(I2C_Address_EEPROM, REG_EEPROM_TotalKB, 2));  //同时向变量赋值
    Serial.println(" KByte.");
    Serial.println();
  }
  else if(command == Command_EEPROM_Clear)  //清空EEPROM中所有数据
  {
    DATA_EEPROM_DataUsed = 0;
    EEP.write(I2C_Address_EEPROM, REG_EEPROM_DataUsed, (unsigned int)0);  //清零已使用数据个数
    delay(10);

    Serial.println("EEPROM clear OK.");
    Serial.println();
  }
  else if(command == Command_PowerSwitch_Radio_OPEN)  //强制开启无线模块电源
  {
    PowerSwitch_Radio_Enable = !PowerSwitch_Radio_Enable;
    if(PowerSwitch_Radio_Enable) Serial.println("Radio open.");
    else Serial.println("Radio close.");
    Serial.println();
  }
  else if(command == Command_NoSleep)  //强制开启无线模块电源
  {
    NoSleep = !NoSleep;
    if(NoSleep) Serial.println("Sleep is disenable.");
    else Serial.println("Sleep is enable.");
    Serial.println();
  }
  else if(command == Command_EEPROM_WritePresent)  //将当前数据写入EEPROM
  {
    EEPROM_Write();
  }

  Close_EEPROM();  //关闭EEPROM电源
}

////光照度 读取//////////////////////////////////////////////
void BH1751FVI_Read()
{
  unsigned long _Result = 0;
  //发送测量命令
  I2C.write(I2C_Address_BH1751FVI, BH1751FVI_Mode);  //发送测量模式命令
  for(unsigned char i = 0; i < 15; i++)
  {
    //读取数据
    delay(130);  //等待转换结果
    _Result += I2C.read(I2C_Address_BH1751FVI, 2);  //回传测量结果,2个字节
  }
  BH1751FVI_Result = _Result/15;  //计算平均值
  //关闭BH1751FVI芯片
  I2C.write(I2C_Address_BH1751FVI, Param_BMP085_Close);  //发送关闭命令
  //计算最终值
  unsigned int tmp = 0;
  for(unsigned char i = 0; i < 16; i++) tmp += pow(2*((BH1751FVI_Result>>i)&1), i);
  //返回测量结果
  BH1751FVI_Result = tmp/1.2;
}

////气压值 读取//////////////////////////////////////////////

//读校准值
void BMP085_init()
{
  ac1 = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xAA, 2);
  ac2 = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xAC, 2);
  ac3 = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xAE, 2);
  ac4 = I2C.read(I2C_Address_BMP085, (unsigned char)0xB0, 2);
  ac5 = I2C.read(I2C_Address_BMP085, (unsigned char)0xB2, 2);
  ac6 = I2C.read(I2C_Address_BMP085, (unsigned char)0xB4, 2);
  b1  = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xB6, 2);
  b2  = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xB8, 2);
  mb  = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xBA, 2);
  mc  = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xBC, 2);
  md  = (int)I2C.read(I2C_Address_BMP085, (unsigned char)0xBE, 2);
}
//读取温度(返回真实值的10倍)
void BMP085_read_Temperature()
{
  unsigned int ut;
  unsigned long ut_clac = 0;
  for(unsigned char i = 0; i < 3; i++)
  {
    //读取未补偿的温度值(每秒一次)
    I2C.write(I2C_Address_BMP085, REG_BMP085_Command, Param_BMP085_Temperature);  //请求读取温度
    delay(5);  //至少等待4.5ms
    //从测量值寄存器读取两个字节
    ut = I2C.read(I2C_Address_BMP085, REG_BMP085_MSB, 2);
    ut_clac += ut;
    delay(1000);
  }
  ut = ut_clac/3;  //计算平均值
  //计算温度值
  b5 = ((((long)ut - (long)ac6)*(long)ac5) >> 15) + (((long)mc << 11)/(((((long)ut - (long)ac6)*(long)ac5) >> 15) + md));
  BMP085_Temperature = ((b5 + 8)>>4);
}
//读取气压(Pa)
void BMP085_read_Pressure()
{
  unsigned long up, up_clac = 0, b4;
  long b3, p;
  for(unsigned char i = 0; i < 64; i++)
  {
    //读取未补偿的压力值(最多每秒128次)
    I2C.write(I2C_Address_BMP085, REG_BMP085_Command, (unsigned char)(0x34 + (OSS<<6)));  //写请求读取气压
    //等待转换,延迟时间依赖于OSS
    delay(2 + (3<<OSS));
    //读取测量值寄存器
    I2C.write(I2C_Address_BMP085, REG_BMP085_MSB);
    Wire.beginTransmission(I2C_Address_BMP085);
    Wire.requestFrom(I2C_Address_BMP085, 3);
    up = (((unsigned long) Wire.read() << 16) | ((unsigned long) Wire.read() << 8) | (unsigned long) Wire.read()) >> (8-OSS);
    Wire.endTransmission();
    up_clac += up;
    delay(10);
  }
  up = up_clac/64;  //计算平均值
  //计算气压值
  b3 = (ac4 * (unsigned long)((((((ac3 * (b5-4000))>>13) + (((b5-4000) * (((b5-4000) * (b5-4000))>>12))>>16)) + 2)>>2) + 32768))>>15;
  b4 = ((unsigned long)(up - ((((((long)ac1)*4 + (((b2 * ((b5-4000) * (b5-4000))>>12)>>11) + ((ac2 * (b5-4000))>>11)))<<OSS) + 2)>>2)) * (50000>>OSS));
  if(b4 < 0x80000000) p = (b4<<1)/b3;
  else p = (b4/b3)<<1;
  BMP085_Pressure = p + ((((((p>>8) * (p>>8)) * 3038)>>16) + ((-7357 * p)>>16) + 3791)>>4);
}

////地磁方向 读取 (没有校准过程)//////////////////////////////////////////////

void HMC5883L_init()
{
  I2C.write(I2C_Address_HMC5883L, REG_HMC5883L_SetA, (unsigned char)0x78);  //配置寄存器A,平均采样数8、输出速率75Hz
  I2C.write(I2C_Address_HMC5883L, REG_HMC5883L_SetB, (unsigned char)0x20);  //配置寄存器B,增益1090高斯
  I2C.write(I2C_Address_HMC5883L, REG_HMC5883L_Mode, (unsigned char)0x00);  //模式寄存器,连续测量模式
}
void HMC5883L_Read()
{
  int _x, _y, _z;

  for(unsigned char i = 0; i < 3; i++)
  {
    //发送测量命令
    I2C.write(I2C_Address_HMC5883L, REG_HMC5883L_X);
    delay(10);  //等待转换结果
    //接收6个字节
    Wire.beginTransmission(I2C_Address_HMC5883L);
    Wire.requestFrom(I2C_Address_HMC5883L, 6);
    _x = Wire.read()<<8 | Wire.read();  //X轴数据
    _z = Wire.read()<<8 | Wire.read();  //Z轴数据
    _y = Wire.read()<<8 | Wire.read();  //Y轴数据
    Wire.endTransmission();
    delay(50);
  }
  //计算地磁方向
  HMC5883L_Result = ((atan2(_y, _x) + M_PI) * 180 / M_PI);
  //纠正磁偏角
  HMC5883L_Result -= HMC5883L_Declination;
  if(HMC5883L_Result < 0) HMC5883L_Result = 360 - HMC5883L_Result;
  else if(HMC5883L_Result > 359) HMC5883L_Result -= 360;
  //将HMC5883L芯片设置到闲置模式
  I2C.write(I2C_Address_HMC5883L, REG_HMC5883L_Mode, (unsigned char)0x01);
}

////温湿度 读取//////////////////////////////////////////////

bool AM2302_Read()  //读取AM2302数据值,返回读取是成功
{
  //注意:两次读取间隔至少2s,长时间未读取的话,第一次值不能用,因为读到的是上一次读取完后模块自己读的值

  unsigned char CRC;

  for(unsigned char i = 0; i < 3; i++)
  {
    AM2302_Temperature = 0;
    AM2302_Humidity = 0;
    CRC = 0;  //CRC校验值(读取出来的)

    pinMode(AM2302_Pin, OUTPUT);    //设置为输出状态
    digitalWrite(AM2302_Pin, LOW);  //输出低电平
    delay(18);
    pinMode(AM2302_Pin, INPUT);     //设置为输入状态

    if(pulseIn(AM2302_Pin, HIGH));  //等待80us高电平,80ms或50ms的低电平不管它

    //读取数据
    for(char i = 15; i >= 0; i--)  //读16bit的湿度值
    {
      if(pulseIn(AM2302_Pin, HIGH) > 60) bitSet(AM2302_Humidity, i);  //读取26us或70us高电平
    }
    for(char i = 15; i >= 0; i--)  //读16bit的温度值
    {
      if(pulseIn(AM2302_Pin, HIGH) > 60) bitSet(AM2302_Temperature, i);  //读取26us或70us高电平
    }
    for(char i = 7; i >= 0; i--)  //读8bit的CRC值
    {
      if(pulseIn(AM2302_Pin, HIGH) > 60) bitSet(CRC, i);  //读取26us或70us高电平
    }
    delay(2000);
  }

  //校验CRC
  unsigned int CRC_tmp = 0;  //CRC校验计算值
  for(char i = 15; i >= 0; i--)
  {
    CRC_tmp = (AM2302_Humidity >> 8) + (AM2302_Humidity & 0x00FF) + (AM2302_Temperature >> 8) + (AM2302_Temperature & 0x00FF);
  }

  //修正负温度
  if(AM2302_Temperature < 0) AM2302_Temperature = -(AM2302_Temperature&0x7FFF);

  if((CRC_tmp & 0x00FF) == CRC) return 1;  //注:校验计算值可能会超出8位,但CRC读取值只有8位,故将8位以上的数据值过滤掉
  else return 0;
}
/*
@end
*/