diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..b511041 Binary files /dev/null and b/.DS_Store differ diff --git a/AcaiaArduinoBLE.cpp b/AcaiaArduinoBLE.cpp index 5813d38..81ff4fc 100644 --- a/AcaiaArduinoBLE.cpp +++ b/AcaiaArduinoBLE.cpp @@ -31,21 +31,24 @@ AcaiaArduinoBLE::AcaiaArduinoBLE(bool debug){ _currentWeight = 0; _connected = false; _packetPeriod = 0; + _isScanning = false; } bool AcaiaArduinoBLE::init(String mac){ - unsigned long start = millis(); _lastPacket = 0; - if (mac == ""){ - BLE.scan(); - }else if (!BLE.scanForAddress(mac)){ + if (mac == "") { + if (!_isScanning) { + BLE.scan(); + _isScanning = true; + } + } else if (!BLE.scanForAddress(mac)) { Serial.print("Failed to find "); Serial.println(mac); + _isScanning = false; return false; } - do{ BLEDevice peripheral = BLE.available(); if(_debug && peripheral){ @@ -61,6 +64,7 @@ bool AcaiaArduinoBLE::init(String mac){ if (peripheral && isScaleName(peripheral.localName())) { BLE.stopScan(); + _isScanning = false; Serial.println("Connecting ..."); if (peripheral.connect()) { @@ -143,24 +147,39 @@ bool AcaiaArduinoBLE::init(String mac){ _packetPeriod = 0; return true; } - }while(millis() - start < 10000); - - Serial.println("failed to find scale"); + return false; } bool AcaiaArduinoBLE::tare(){ - if(_write.writeValue((_type == GENERIC ? TARE_GENERIC : TARE_ACAIA), 6)){ - Serial.println("tare write successful"); - return true; - }else{ + if(_connected == false) return false; + + if (!_write || !_write.canWrite()) { + Serial.println("WRITE characteristic is invalid!"); + _connected = false; + return false; + } + + if (_write.writeValue((_type == GENERIC ? TARE_GENERIC : TARE_ACAIA), 6)){ + Serial.println("tare write successful"); + return true; + } else { _connected = false; Serial.println("tare write failed"); return false; } } + bool AcaiaArduinoBLE::startTimer(){ + if(_connected == false) return false; + + if (!_write || !_write.canWrite()) { + Serial.println("WRITE characteristic is invalid or not writable!"); + _connected = false; + return false; + } + if(_write.writeValue((_type == GENERIC ? START_TIMER_GENERIC : START_TIMER), (_type == GENERIC ? 6 : 7))){ Serial.println("start timer write successful"); @@ -173,6 +192,15 @@ bool AcaiaArduinoBLE::startTimer(){ } bool AcaiaArduinoBLE::stopTimer(){ + if(_connected == false) return false; + + if (!_write || !_write.canWrite()) { + Serial.println("WRITE characteristic is invalid or not writable!"); + _connected = false; + return false; + } + + if(_write.writeValue((_type == GENERIC ? STOP_TIMER_GENERIC : STOP_TIMER), (_type == GENERIC ? 6 : 7 ))){ Serial.println("stop timer write successful"); @@ -185,6 +213,14 @@ bool AcaiaArduinoBLE::stopTimer(){ } bool AcaiaArduinoBLE::resetTimer(){ + if(_connected == false) return false; + + if (!_write || !_write.canWrite()) { + Serial.println("WRITE characteristic is invalid or not writable!"); + _connected = false; + return false; + } + if(_write.writeValue((_type == GENERIC ? RESET_TIMER_GENERIC : RESET_TIMER), (_type == GENERIC ? 6 : 7 ))){ Serial.println("reset timer write successful"); @@ -197,6 +233,14 @@ bool AcaiaArduinoBLE::resetTimer(){ } bool AcaiaArduinoBLE::heartbeat(){ + if(_connected == false) return false; + + if (!_write || !_write.canWrite()) { + Serial.println("WRITE characteristic is invalid or not writable!"); + _connected = false; + return false; + } + if(_write.writeValue(HEARTBEAT, 7)){ _lastHeartBeat = millis(); return true; @@ -219,82 +263,79 @@ bool AcaiaArduinoBLE::heartbeatRequired(){ bool AcaiaArduinoBLE::isConnected(){ return _connected; } -bool AcaiaArduinoBLE::newWeightAvailable(){ + +bool AcaiaArduinoBLE::newWeightAvailable() { + static byte input[20]; // maximum expected size + static const float pow10[] = {1, 10, 100, 1000, 10000, 100000}; // precomputed powers + bool newWeightPacket = false; - //check how long its been since we last got a response - if(_lastPacket && millis()-_lastPacket > MAX_PACKET_PERIOD_MS){ + // Timeout check + if (_lastPacket && millis() - _lastPacket > (_type == GENERIC? MAX_PACKET_PERIOD_GENERIC_MS : MAX_PACKET_PERIOD_ACAIA_MS)) { Serial.println("timeout!"); - //reset connection _connected = false; - BLE.disconnect(); + if (BLE.connected()) { + BLE.disconnect(); + } return false; - }else if(_read.valueUpdated()){ - byte input[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; + } + + if (_read.valueUpdated()) { int l = _read.valueLength(); - // Get packet - if(10 >= l || //10 byte packets for pre-2021 lunar - (13 >= l && OLD != _type) || //13 byte packets for pyxis and older lunar 2021 fw - (14 == l && OLD == _type) || //14 byte packets for lunar 2021 AL008 - (17 == l && NEW == _type) || //17 byte packets for newer lunar 2021 fw - (20 == l && GENERIC == _type) //18 byte packets for generic scales - ){ - _read.readValue(input, (l > 13) ? 13 : l); // readValue() seems to crash whenever l > weight packet (10, 13 or 18) + if ((l >= 10 && l <= 20) && _read) { + // Clear previous data + memset(input, 0, sizeof(input)); + _read.readValue(input, (l > 20) ? 20 : l); - if(_debug){ + if (_debug) { + Serial.print("Packet (len "); Serial.print(l); - Serial.print(": 0x"); - + Serial.print("): 0x"); printData(input, l); Serial.println(); } - } - // Parse New style data packet - if (NEW == _type && (13 == l || 17 == l) && input[4] == 0x05) - { - //Grab weight bytes (5 and 6) - // apply scaling based on the unit byte (9) - // get sign byte (10) - _currentWeight = (((input[6] & 0xff) << 8) + (input[5] & 0xff)) - / pow(10,input[9]) - * ((input[10] & 0x02) ? -1 : 1); - newWeightPacket = true; - - // Parse old style data packet - }else if( OLD == _type && (l == 10 || l == 14)){ - //Grab weight bytes (2 and 3), - // apply scaling based on the unit byte (6) - // get sign byte (7) - _currentWeight = (((input[3] & 0xff) << 8) + (input[2] & 0xff)) - / pow(10, input[6]) - * ((input[7] & 0x02) ? -1 : 1); - newWeightPacket = true; - - }else if( GENERIC == _type && l == 20){ - //Grab weight bytes (3-8), - // get sign byte (2) - _currentWeight = (( input[7] << 16) | (input[8] << 8) | input[9]); - - if (input[6] == 45) { // Check if the value is negative - _currentWeight = -_currentWeight; + // NEW type + if (_type == NEW && (l == 13 || l == 17) && input[4] == 0x05) { + uint16_t rawWeight = ((input[6] & 0xff) << 8) | (input[5] & 0xff); + byte scaleIndex = input[9]; + float scale = (scaleIndex < sizeof(pow10) / sizeof(pow10[0])) ? pow10[scaleIndex] : 1.0; + _currentWeight = rawWeight / scale * ((input[10] & 0x02) ? -1 : 1); + newWeightPacket = true; + + // OLD type + } else if (_type == OLD && (l == 10 || l == 14)) { + uint16_t rawWeight = ((input[3] & 0xff) << 8) | (input[2] & 0xff); + byte scaleIndex = input[6]; + float scale = (scaleIndex < sizeof(pow10) / sizeof(pow10[0])) ? pow10[scaleIndex] : 1.0; + _currentWeight = rawWeight / scale * ((input[7] & 0x02) ? -1 : 1); + newWeightPacket = true; + + // GENERIC type + } else if (_type == GENERIC && l == 20) { + int32_t raw = (input[7] << 16) | (input[8] << 8) | input[9]; + if (input[6] == 45) { + raw = -raw; } - _currentWeight = _currentWeight / 100; - newWeightPacket = true; - } - if(newWeightPacket){ - if(_lastPacket){ - _packetPeriod = millis() - _lastPacket; + _currentWeight = raw / 100.0f; + newWeightPacket = true; + } + + // Timestamp and delta + if (newWeightPacket) { + if (_lastPacket) { + _packetPeriod = millis() - _lastPacket; + } + _lastPacket = millis(); } - _lastPacket = millis(); } - return newWeightPacket; - } - else{ - return false; } + + return newWeightPacket; } + + bool AcaiaArduinoBLE::isScaleName(String name){ String nameShort = name.substring(0,5); diff --git a/AcaiaArduinoBLE.h b/AcaiaArduinoBLE.h index 56cb565..4d88367 100644 --- a/AcaiaArduinoBLE.h +++ b/AcaiaArduinoBLE.h @@ -19,7 +19,10 @@ #define WRITE_CHAR_GENERIC "ff12" #define READ_CHAR_GENERIC "ff11" #define HEARTBEAT_PERIOD_MS 2750 -#define MAX_PACKET_PERIOD_MS 5000 +#ifndef MAX_PACKET_PERIOD_GENERIC_MS + #define MAX_PACKET_PERIOD_GENERIC_MS 500 +#endif +#define MAX_PACKET_PERIOD_ACAIA_MS 2000 #include "Arduino.h" #include @@ -61,6 +64,7 @@ class AcaiaArduinoBLE{ bool _debug; long _packetPeriod; long _lastPacket; + bool _isScanning; }; #endif diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000..e9ca7a9 Binary files /dev/null and b/examples/.DS_Store differ diff --git a/examples/bare_minimum/.DS_Store b/examples/bare_minimum/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/examples/bare_minimum/.DS_Store differ diff --git a/examples/bare_minimum/bare_minimum.ino b/examples/bare_minimum/bare_minimum.ino deleted file mode 100644 index 854e4c9..0000000 --- a/examples/bare_minimum/bare_minimum.ino +++ /dev/null @@ -1,30 +0,0 @@ -#include - -#define DEBUG false - -AcaiaArduinoBLE scale(DEBUG); -void setup() { - Serial.begin(115200); - while (!Serial) {} - Serial.println("Scale Interface test"); - - // initialize the Bluetooth® Low Energy hardware - BLE.begin(); - // Optionally add your Mac Address as an argument: acaia.init("##:##:##:##:##:##"); - scale.init(); - scale.tare(); - scale.tare(); -} - -void loop() { - // Send a heartbeat message to the acaia periodically to maintain connection - if (scale.heartbeatRequired()) { - scale.heartbeat(); - } - - // always call newWeightAvailable to actually receive the datapoint from the scale, - // otherwise getWeight() will return stale data - if (scale.newWeightAvailable()) { - Serial.println(scale.getWeight()); - } -} diff --git a/examples/bare_minimum/main.cpp b/examples/bare_minimum/main.cpp new file mode 100644 index 0000000..dcb8537 --- /dev/null +++ b/examples/bare_minimum/main.cpp @@ -0,0 +1,65 @@ +#include + +//The timeout has been set very tightely for "generic" scales. The Bookoo Themis Mini handles 500ms. If you have issues with your scale giving a timeout use, increase before you call the header. +//#define MAX_PACKET_PERIOD_GENERIC_MS 1000 +#include + +//true doesnt work... +#define DEBUG false + +bool scaleConnected = false; +unsigned long lastTare = 0; + + +AcaiaArduinoBLE scale(DEBUG); +uint8_t goalWeight = 100; // Goal Weight to be read from EEPROM + +void setup() { + Serial.begin(115200); + while (!Serial) {} + Serial.println("Scale Interface test"); + + // initialize the Bluetooth® Low Energy hardware + BLE.begin(); + // Optionally add your Mac Address as an argument: acaia.init("##:##:##:##:##:##"); + + //this is basically not useful, since it will always connect during 'loop()' + if(scale.init()) { + scale.tare(); + scale.tare(); + } +} + +void loop() { + // reconnect the scale if its not connected. Note this is an expensive function. Has a self reset of 50ms. + if(scale.isConnected() == false) { + scale.init(); + } + + // Send a heartbeat message to the scale periodically to maintain connection if necessary. + if (scale.heartbeatRequired()) { + scale.heartbeat(); + } + + // always call newWeightAvailable to actually receive the datapoint from the scale, + // otherwise getWeight() will return stale data + if (scale.isConnected() && scale.newWeightAvailable()) { + Serial.println(scale.getWeight()); + } + + // To test ; tare the scale every 10 seconds + // This is not necessary, but useful for testing purposes + if(lastTare + 10000 < millis()) { + lastTare = millis(); + //there is a limit to the amount of commands you can send within a timeframe. The library does not handle this. YMMV + //note that "tare" temporary locks up the BOOKOO weight untill its stabilized. + //Acaia tares almost instantly, even when not stable. Note this may cause the tare not to be zeroed properly when the weight is not stable. + scale.tare(); + delay(50); + scale.stopTimer(); + delay(50); + scale.resetTimer(); + delay(50); + scale.startTimer(); + } +} \ No newline at end of file