From 3761411bb4f23dd0ffa6831641597ae9b638a351 Mon Sep 17 00:00:00 2001 From: isPointless <105437935+isPointless@users.noreply.github.com> Date: Sun, 22 Jun 2025 14:35:46 +0200 Subject: [PATCH 1/2] Connection loop update, shorten timings Changed the init() function to be called from within an .isConnected() if-statement inside LOOP. This increases the disconnect - reconnect performance drastically. scale.isConnected() is also much more up to date this way. --- .DS_Store | Bin 0 -> 6148 bytes AcaiaArduinoBLE.cpp | 173 +++++++++++++++---------- AcaiaArduinoBLE.h | 5 +- examples/.DS_Store | Bin 0 -> 6148 bytes examples/bare_minimum/.DS_Store | Bin 0 -> 6148 bytes examples/bare_minimum/bare_minimum.ino | 30 ----- examples/bare_minimum/main.cpp | 65 ++++++++++ 7 files changed, 175 insertions(+), 98 deletions(-) create mode 100644 .DS_Store create mode 100644 examples/.DS_Store create mode 100644 examples/bare_minimum/.DS_Store delete mode 100644 examples/bare_minimum/bare_minimum.ino create mode 100644 examples/bare_minimum/main.cpp diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b51104114223776529a3d5c16c086e20b312fc36 GIT binary patch literal 6148 zcmeHK%}T>S5Z<-brWBzEg&r5Y7HkUj2QMMk7cim+m70*E!8BW%)Ci@Jv%Zi|;`2DO zy8%mc@FZeqVDrt+&+g6#*&oIjcjuwaSer3sK||!IR0x_YU0Ws?k*hJLEM{Yt26;NI z80asWaP1bX`IY%>!oICPh-Co2k6;={Y1Zp~@~v8Zd#3@?YTSEIGWRk+o2RZnyT#s> zlyOk%esCR)^HF>6Tqc3lgWX1CA$Vocn4*zt`~2X#4>pVUzu0N6A}Z& z05L!eYz_nF46s_8Q!-Ub3=ji9WB~UE0S(bHSZGvR2Ym4QjQ%JbCPz&~Sv*N5KFg`&*ax>g>ZwF24$G!%?0Pyqpb?V Zh;s}U8gUe~t8_rR2q;46Lk#=^178q7O& millis()) return false; + lastCall = millis(); + _lastPacket = 0; if (mac == ""){ @@ -45,7 +48,6 @@ bool AcaiaArduinoBLE::init(String mac){ return false; } - do{ BLEDevice peripheral = BLE.available(); if(_debug && peripheral){ @@ -143,24 +145,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 +190,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 +211,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 +231,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 +261,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..0053816 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 diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e9ca7a97da26fe7cbd10fe89c4136bae14e060a3 GIT binary patch literal 6148 zcmeHK%}T>S5T0$TZK*;J3Oz1(E!Y(54_-p8FJMFuDlwr&gK4&;tv!@N&iX<=iO=KA z?nW%N7Y`z324=t6*_kZ!CG2DYKy;^l2cQlB6;#4P6^9>$)=5{SU_FFFKf{M1M3A5k zSEAYR9~q!`w*w&rkir1IzrQIA!`XNg#wx^^_iz+P<4LRaAqvIP)^-`CR=)S1)XbX% zlj*n{q_;G?QYsE}I|#3%!OXAkovUOLM9Co42~mH5A$K=X(pR&tnkN0R&h^Yf6~(VN z=JQU+X-daASv2MRwB2gTqvQ5sQLXGB9G+eJ&+#x(uZC2C-=CIUi#fcZv9`V!FHK^V zJYr0qpXV_$1Iz$3u*nSAGp|zHH1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 - -#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 From 212bf63caa31f8456e0b894b26bff9a61475464a Mon Sep 17 00:00:00 2001 From: isPointless <105437935+isPointless@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:36:03 +0200 Subject: [PATCH 2/2] Added a scanning flag to remove the timer The new 'isScanning' flag guards the scan state meaning that it won't restart a scan. --- AcaiaArduinoBLE.cpp | 16 +++++++++------- AcaiaArduinoBLE.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/AcaiaArduinoBLE.cpp b/AcaiaArduinoBLE.cpp index 8db239b..81ff4fc 100644 --- a/AcaiaArduinoBLE.cpp +++ b/AcaiaArduinoBLE.cpp @@ -31,20 +31,21 @@ AcaiaArduinoBLE::AcaiaArduinoBLE(bool debug){ _currentWeight = 0; _connected = false; _packetPeriod = 0; + _isScanning = false; } bool AcaiaArduinoBLE::init(String mac){ - static unsigned long lastCall = millis(); - if(lastCall + 50 > millis()) return false; - lastCall = 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; } @@ -63,6 +64,7 @@ bool AcaiaArduinoBLE::init(String mac){ if (peripheral && isScaleName(peripheral.localName())) { BLE.stopScan(); + _isScanning = false; Serial.println("Connecting ..."); if (peripheral.connect()) { diff --git a/AcaiaArduinoBLE.h b/AcaiaArduinoBLE.h index 0053816..4d88367 100644 --- a/AcaiaArduinoBLE.h +++ b/AcaiaArduinoBLE.h @@ -64,6 +64,7 @@ class AcaiaArduinoBLE{ bool _debug; long _packetPeriod; long _lastPacket; + bool _isScanning; }; #endif