hxbot/HXpower/HXpower.ino

825 lines
17 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/////////////////////////////////////////
// HXbot HXpower firmware /
// EoF 2016 EoF@itphx.ru /
/////////////////////////////////////////
#include <Wire.h>
// DEBUG
#define DEBUG 0
// DEFINE
#define SLAVE_ADDRESS 0x05
#define XOR_SEQ 0xFF
#define EXT_COM 0xAA
#define VIN_PIN 0
#define VDC_PIN 1
#define VBT_PIN 2
#define VBA_PIN 3
#define R_BL 7
#define R_PF 6
#define R_HX 5
#define R_IS 4
#define BL_RED_PIN 8
#define BL_GRN_PIN 9
#define BTN_PIN 2
#define LED_PIN 3
#define REP_COUNT 5
#define TIMEOUT 5000
// COMMANDS
// VOLTMETERS
#define COM_GET_VIN 0x01
#define COM_GET_VDC 0x02
#define COM_GET_VBT 0x03
#define COM_GET_VBA 0x04
#define COM_GET_VCC 0x07
// TEMP
#define COM_GET_TEMP 0x06
// STATUS
#define COM_GET_STAT1 0x08
#define COM_GET_STAT2 0x09
// RELAYS
#define COM_POWER_ON_BL 0x11 // 17
#define COM_POWER_ON_PF 0x12 // 18
#define COM_SWITCH_TO_IS 0x13 // 19
#define COM_SWITCH_TO_IN 0x14 // 20
#define COM_SHUTDOWN_BL 0x15 // 21
#define COM_SHUTDOWN_PF 0x16 // 22
#define COM_SWITCH_TO_LR 0x17 // 23
#define COM_SWITCH_TO_BA 0x18 // 24
#define COM_ENABLE_PF 0x1B // 27
#define COM_DISABLE_PF 0x1C // 28
#define COM_ENABLE_BL 0x1D // 29
#define COM_DISABLE_BL 0x1E // 30
#define OK_RSP 0x00
#define NO_RSP 0xFF
#define ERR_RSP 0x01
#define BLK_RSP 0x02
#define CSE_RSP 0x03
#define IOE_RSP 0x04
#define TMO_RSP 0x05
// CONST
#define DELAY_TIME 900
#define BA_TIMEOUT 3000
#define BA_BLOCK_TIMEOUT 30000
#define BUTTON_CYCLES 4
#define V_CHECK_CYCLES 3
#define VIN_MIN 10.0
#define VIN_MAX 14.0
#define VDC_MIN 4.8
#define VDC_MAX 6.0
#define VBT_MIN 3.0
#define VBT_MAX 4.25
#define VBA_MIN 6.0
#define VBA_MAX 8.4
#define VCC_MIN 4.5
#define VCC_MAX 5.3
// VAR
// резисторы делителя напряжения
const float r1_1 = 200000; // 200K
const float r1_2 = 100000; // 100K
const float r2_1 = 100000; // 100K
const float r2_2 = 100000; // 100K
const float r3_1 = 100000; // 100K
const float r3_2 = 100000; // 100K
const float r4_1 = 200000; // 200K
const float r4_2 = 100000; // 100K
// эту константу (typVbg) необходимо откалибровать индивидуально
const float typVbg = 1.083; // 1.0 -- 1.2
float vcc = 0.0;
float Vmax1, Vmax2, Vmax3, Vmax4;
float kin, kdc, kbt, kba;
float vin, vdc, vbt, vba;
float tin, tdc, tbt, tba;
//char *stime;
byte cmd = 0x00;
byte ext = 0x00;
byte sum = 0x00;
unsigned long lastCommandTime = 0;
byte autoResponse = 0x00;
byte byteResponse = 0x00;
boolean needByte = false;
boolean needSumm = false;
float floatResponse = 0.0;
boolean needFloat = false;
byte floatByte = 0;
byte ba_count = 0;
unsigned long ba_time;
boolean ba_blocked = false;
byte button_count = 0;
boolean vin_ok = false;
byte vin_count = 0;
boolean vdc_ok = false;
byte vdc_count = 0;
boolean vbt_ok = false;
byte vbt_count = 0;
boolean vba_ok = false;
byte vba_count = 0;
boolean vcc_ok = false;
byte vcc_count = 0;
boolean ba_enabled = true;
boolean lr_enabled = true;
boolean pf_enabled = false;
boolean bl_enabled = false;
boolean bl_powered = false;
boolean bl_error = false;
boolean ba_full = false;
boolean ba_charge = false;
byte status_1 = 0x00;
byte status_2 = 0x00;
// Loop delay
int delay_time = DELAY_TIME;
/****************************************************************************
* Главная программа
****************************************************************************/
void setup() {
if (DEBUG) {
Serial.begin(9600);
Serial.println("---");
delay(1000);
}
// определение опорного напряжения
analogReference(DEFAULT); // DEFAULT INTERNAL использовать vcc как AREF
delay(100);
kin = r1_2 / (r1_1 + r1_2);
kdc = r2_2 / (r2_1 + r2_2);
kbt = r3_2 / (r3_1 + r3_2);
kba = r4_2 / (r4_1 + r4_2);
vcc = readvcc();
Vmax1 = vcc / kin;
Vmax2 = vcc / kdc;
Vmax3 = vcc / kbt;
Vmax4 = vcc / kba;
if (DEBUG) {
Serial.print("Vcc= ");
Serial.println(vcc);
Serial.print("Vmax1= ");
Serial.println(Vmax1);
Serial.print("Vmax2= ");
Serial.println(Vmax2);
Serial.print("Vmax3= ");
Serial.println(Vmax3);
Serial.print("Vmax4= ");
Serial.println(Vmax4);
Serial.println("---");
}
// Initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// Define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(answer);
pinMode(R_BL, OUTPUT);
pinMode(R_PF, OUTPUT);
pinMode(R_HX, OUTPUT);
pinMode(R_IS, OUTPUT);
pinMode(BTN_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BL_RED_PIN, INPUT);
pinMode(BL_GRN_PIN, INPUT);
ba_time = millis();
}
void loop() {
// Timeout protection
if ((lastCommandTime + TIMEOUT < millis())) {
floatResponse = 0.0;
floatByte = 0;
needFloat = false;
autoResponse = TMO_RSP;
}
//vcc = readvcc();
// stime = TimeToString(millis()/1000);
// считываем точное напряжение с A0, где будет находиться наш вольтметр с делителем напряжения
tin = 0.0;
tdc = 0.0;
tbt = 0.0;
tba = 0.0;
for (byte i = 0; i < REP_COUNT; i++) {
tin = tin * readvcc() + analogRead(VIN_PIN);
tdc = tdc * readvcc() + analogRead(VDC_PIN);
tbt = tbt * readvcc() + analogRead(VBT_PIN);
tba = tba * readvcc() + analogRead(VBA_PIN);
delay(10);
}
// tin = (tin * vcc) / 1024.0 / kin / REP_COUNT;
// tdc = (tdc * vcc) / 1024.0 / kdc / REP_COUNT;
// tbt = (tbt * vcc) / 1024.0 / kbt / REP_COUNT;
// tba = (tba * vcc) / 1024.0 / kba / REP_COUNT;
tin = tin / 1024.0 / kin / REP_COUNT;
tdc = tdc / 1024.0 / kdc / REP_COUNT;
tbt = tbt / 1024.0 / kbt / REP_COUNT;
tba = tba / 1024.0 / kba / REP_COUNT;
vin = tin;
vdc = tdc;
vbt = tbt;
if (ba_enabled) vba = tba;
// if (DEBUG && count >= 5) {
// Serial.print("Vcc= ");
// Serial.println(vcc);
// Serial.print("Vin= ");
// Serial.println(vin);
// Serial.print("Vdc= ");
// Serial.println(vdc);
// Serial.print("Vbt= ");
// Serial.println(vbt);
// Serial.print("Vpf= ");
// Serial.println(vpf);
// Serial.println("---");
// count = 0;
// }
// else count++;
// Vin check
if (vin >= VIN_MIN && vin <= VIN_MAX) {
if (! vin_ok) vin_count++;
}
else {
vin_count = 0;
vin_ok = false;
}
if (vin_count >= V_CHECK_CYCLES) vin_ok = true;
// Vdc check
if (vdc >= VDC_MIN && vdc <= VDC_MAX) {
if (! vdc_ok) vdc_count++;
}
else {
vdc_count = 0;
vdc_ok = false;
}
if (vdc_count >= V_CHECK_CYCLES) vdc_ok = true;
// Vbt check
if (vbt >= VBT_MIN && vbt <= VBT_MAX) {
if (! vbt_ok) vbt_count++;
}
else {
vbt_count = 0;
vbt_ok = false;
}
if (vbt_count >= V_CHECK_CYCLES) vbt_ok = true;
// Vba check
if (ba_enabled) {
if (vba >= VBA_MIN && vba <= VBA_MAX) {
if (! vba_ok) vba_count++;
}
else {
vba_count = 0;
vba_ok = false;
}
if (vba_count >= V_CHECK_CYCLES) vba_ok = true;
}
else {
//vba = 0.0;
vba_ok = false;
}
// Vcc check
if (vcc >= VCC_MIN && vcc <= VCC_MAX) {
if (! vcc_ok) vcc_count++;
}
else {
vcc_count = 0;
vcc_ok = false;
}
if (vcc_count >= V_CHECK_CYCLES) vcc_ok = true;
// Auto switch to BA
if (! vin_ok) {
switchToBA();
}
// BL status
bl_powered = false;
bl_error = false;
ba_full = false;
ba_charge = false;
if (vin_ok && bl_enabled) {
delay_time = 0;
// if (DEBUG) {
// unsigned long tmp1, tmp2;
//
// tmp1 = millis();
// while(digitalRead(BL_GRN_PIN) == LOW) {
//
// }
// tmp2 = millis();
//
// Serial.print("time1: ");
// Serial.println(tmp2 - tmp1);
//
// tmp1 = millis();
// while(digitalRead(BL_GRN_PIN) == HIGH) {
//
// }
// tmp2 = millis();
// Serial.print("time2: ");
// Serial.println(tmp2 - tmp1);
//
// }
switch (detectBlink(BL_GRN_PIN)) {
case 0:
ba_full = false;
ba_charge = false;
break;
case 1:
ba_full = true;
ba_charge = false;
bl_powered = true;
break;
case 2:
ba_full = false;
ba_charge = true;
bl_powered = true;
break;
}
if (! ba_charge && ! ba_full) {
switch (detectBlink(BL_RED_PIN)) {
case 0:
bl_powered = false;
bl_error = false;
break;
case 1:
bl_powered = true;
bl_error = false;
break;
case 2:
bl_powered = false;
bl_error = true;
break;
}
}
} else delay_time = DELAY_TIME;
// Button reaction
if (digitalRead(BTN_PIN) == HIGH) {
if (button_count >= BUTTON_CYCLES) {
if (ba_enabled && vin_ok) {
switchToIN();
}
else {
switchToBA();
ba_blocked = true;
}
button_count = 0;
}
else button_count++;
}
else button_count = 0;
// Disable BA block
if (ba_blocked && ba_time + BA_BLOCK_TIMEOUT < millis()) {
ba_blocked = false;
}
// Auto switch to IN
if (vin_ok && ba_enabled && ! ba_blocked && ba_time + BA_TIMEOUT < millis()) {
switchToIN();
}
// Auto enable BL
if (! pf_enabled && ! ba_enabled && vin_ok) {
enableBL();
}
status_1 = 0x00;
if (vcc_ok) status_1 |= 0x01;
if (vin_ok) status_1 |= 0x02;
if (vdc_ok) status_1 |= 0x04;
if (vbt_ok) status_1 |= 0x08;
if (vba_ok) status_1 |= 0x10;
if (lr_enabled) status_1 |= 0x20;
if (ba_enabled) status_1 |= 0x40;
if (ba_blocked) status_1 |= 0x80;
status_2 = 0x00;
if (bl_powered) status_2 |= 0x01;
if (bl_error) status_2 |= 0x02;
if (ba_full) status_2 |= 0x04;
if (ba_charge) status_2 |= 0x08;
// if (vpf_low) status_2 |= 0x10;
// if (vpf_high) status_2 |= 0x20;
if (pf_enabled) status_2 |= 0x40;
if (bl_enabled) status_2 |= 0x80;
// Delay
delay(delay_time);
}
/****************************************************************************
* Функции
****************************************************************************/
float readvcc() {
byte i;
float result = 0.0;
float tmp = 0.0;
for (i = 0; i < REP_COUNT; i++) {
// Read 1.1V reference against Avcc
// set the reference to vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
// works on an Arduino 168 or 328
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(3); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
tmp = (high<<8) | low;
tmp = (typVbg * 1023.0) / tmp;
result = result + tmp;
delay(5);
}
result = result / REP_COUNT;
return result;
}
// Callback for received data
void receiveData(int byteCount) {
while(Wire.available()) {
// Get command
ext = Wire.read();
if (ext == EXT_COM && byteCount == 3) {
cmd = 0x00;
sum = 0x00;
if (Wire.available()) sum = Wire.read();
if (Wire.available()) cmd = Wire.read();
if ((cmd ^ XOR_SEQ) != sum) {
autoResponse = CSE_RSP;
return;
}
}
else {
autoResponse = ERR_RSP;
while(Wire.available()) {
ext = Wire.read();
}
return;
}
// Process command
switch (cmd) {
case COM_GET_VIN:
sendFloat(vin);
break;
case COM_GET_VDC:
sendFloat(vdc);
break;
case COM_GET_VBT:
sendFloat(vbt);
break;
case COM_GET_VBA:
sendFloat(vba);
break;
case COM_GET_VCC:
sendFloat(vcc);
break;
case COM_GET_TEMP:
sendFloat(getInternalTemp());
break;
case COM_GET_STAT1:
sendByte(status_1);
break;
case COM_GET_STAT2:
sendByte(status_2);
break;
case COM_POWER_ON_BL:
digitalWrite(R_BL, HIGH);
bl_enabled = true;
commandResponse();
break;
case COM_SHUTDOWN_BL:
digitalWrite(R_BL, LOW);
bl_enabled = false;
commandResponse();
break;
case COM_POWER_ON_PF:
digitalWrite(R_PF, HIGH);
pf_enabled = true;
commandResponse();
break;
case COM_SHUTDOWN_PF:
digitalWrite(R_PF, LOW);
pf_enabled = false;
commandResponse();
break;
case COM_SWITCH_TO_IS:
commandResponse(switchToIS());
break;
case COM_SWITCH_TO_LR:
commandResponse(switchToLR());
break;
case COM_SWITCH_TO_IN:
commandResponse(switchToIN());
break;
case COM_SWITCH_TO_BA:
commandResponse(switchToBA());
break;
case COM_ENABLE_PF:
enablePF();
commandResponse();
break;
case COM_DISABLE_PF:
disablePF();
commandResponse();
break;
case COM_ENABLE_BL:
enableBL();
commandResponse();
break;
case COM_DISABLE_BL:
disableBL();
commandResponse();
break;
default:
autoResponse = ERR_RSP;
break;
}
}
}
void commandResponse() {
lastCommandTime = millis();
autoResponse = OK_RSP;
}
void commandResponse(byte response) {
lastCommandTime = millis();
autoResponse = response;
}
void sendByte(byte value) {
lastCommandTime = millis();
byteResponse = value;
needSumm = false;
needByte = true;
}
void sendFloat(float value) {
lastCommandTime = millis();
floatResponse = value;
floatByte = 0;
needFloat = true;
}
byte cSum(byte value) {
return value ^ XOR_SEQ;
}
byte cSum(byte *data, byte dataSize) {
byte tmp = 0x00;
for (byte i = 0; i < dataSize; i++) {
tmp = tmp ^ data[i];
}
return tmp ^ XOR_SEQ;
}
void answer() {
// Want float value?
if (needFloat) {
// Get access to the float as a byte-array:
byte *data = (byte *) &floatResponse;
if (floatByte < sizeof(floatResponse)) {
// Send byte
Wire.write(data[floatByte]);
floatByte++;
}
else {
// Send control sum
Wire.write(cSum(data, sizeof(floatResponse)));
needFloat = false;
}
}
else {
// Want byte value?
if (needByte) {
if (!needSumm) {
// Send byte
Wire.write(byteResponse);
needSumm = true;
}
else {
// Send control sum
Wire.write(cSum(byteResponse));
needSumm = false;
needByte = false;
}
}
else {
// Want something else?
Wire.write(autoResponse);
}
}
// Nothing more to send
autoResponse = NO_RSP;
}
byte switchToLR() {
if (vbt_ok) {
digitalWrite(R_HX, LOW);
lr_enabled = true;
return OK_RSP;
}
else return BLK_RSP;
}
byte switchToIS() {
digitalWrite(R_HX, HIGH);
lr_enabled = false;
return OK_RSP;
}
byte switchToBA() {
if (! ba_enabled) {
// Disable BL
if (bl_enabled) {
digitalWrite(R_BL, LOW);
bl_enabled = false;
//delay(500);
}
digitalWrite(R_IS, LOW);
ba_enabled = true;
digitalWrite(LED_PIN, LOW);
ba_time = millis();
ba_blocked = true;
}
return OK_RSP;
}
byte switchToIN() {
if (vin_ok && ba_enabled) {
digitalWrite(R_IS, HIGH);
ba_enabled = false;
digitalWrite(LED_PIN, HIGH);
return OK_RSP;
}
else return BLK_RSP;
}
void enablePF() {
if (bl_enabled) {
digitalWrite(R_BL, LOW);
bl_enabled = false;
//delay(500);
}
digitalWrite(R_PF, HIGH);
pf_enabled = true;
}
void disablePF() {
digitalWrite(R_PF, LOW);
pf_enabled = false;
}
byte enableBL() {
if (! ba_enabled && ! pf_enabled) {
digitalWrite(R_BL, HIGH);
bl_enabled = true;
return OK_RSP;
}
else return BLK_RSP;
}
void disableBL() {
digitalWrite(R_BL, LOW);
bl_enabled = false;
}
// Get the internal temperature
float getInternalTemp() {
unsigned int wADC;
float t;
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
ADCSRA |= _BV(ADEN); // enable the ADC
delay(20); // wait for voltages to become stable.
ADCSRA |= _BV(ADSC); // Start the ADC
while (bit_is_set(ADCSRA,ADSC));
wADC = ADCW;
t = (wADC - 324.31 ) / 1.22;
return(t);
}
byte detectBlink(byte pin) {
// Return codes:
// 0 - LOW
// 1 - HIGH
// 2 - BLINK
byte low_count = 0;
byte high_count = 0;
byte i = 0;
byte pin_level;
for (i = 0; i < 3; i++) {
pin_level = digitalRead(pin);
if (pin_level == HIGH) high_count++;
if (pin_level == LOW) low_count++;
delay(300);
}
if (low_count > 0 && high_count == 0)
return 0;
else
if (high_count > 0 && low_count == 0)
return 1;
else
return 2;
}