///////////////////////////////////////// // HXbot HXpower firmware / // EoF 2016 EoF@itphx.ru / ///////////////////////////////////////// #include // 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; }