Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 82 additions & 19 deletions AcaiaArduinoBLE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Released into the public domain.

Adding Generic Scale Support, Pio Baettig
Fixing Felicita Arc Bug, A-TWJ

*/
#include "Arduino.h"
#include "AcaiaArduinoBLE.h"
Expand All @@ -21,6 +23,11 @@ byte TARE_GENERIC[6] = { 0x03, 0x0a, 0x01, 0x00, 0x00, 0x08 };
byte START_TIMER_GENERIC[6] = { 0x03, 0x0a, 0x04, 0x00, 0x00, 0x0a };
byte STOP_TIMER_GENERIC[6] = { 0x03, 0x0a, 0x05, 0x00, 0x00, 0x0d };
byte RESET_TIMER_GENERIC[6] = { 0x03, 0x0a, 0x06, 0x00, 0x00, 0x0c };
byte TARE_FELICITA[1] = { 0x54 }; // 'Tare'
byte START_TIMER_FELICITA[1] = { 0x52 }; // 'Start_Timer'
byte STOP_TIMER_FELICITA[1] = { 0x53 }; // 'Stop_Timer'
byte RESET_TIMER_FELICITA[1] = { 0x43 }; // 'Reset_Timer'
byte WEIGHT_TIMER_MODE_FELICITA[1] = { 0x32 }; // 'Weight+timer mode (known-good wake state)

/* Generic commands from
https://github.com/graphefruit/Beanconqueror/blob/master/src/classes/devices/felicita/constants.ts
Expand Down Expand Up @@ -116,6 +123,11 @@ bool AcaiaArduinoBLE::init(String mac){
_type = GENERIC;
_write = peripheral.characteristic(WRITE_CHAR_GENERIC);
_read = peripheral.characteristic(READ_CHAR_GENERIC);
} else if(peripheral.characteristic(READ_CHAR_FELICITA).canSubscribe()){
Serial.println("Felicita Arc Detected");
_type = FELICITA;
_write = peripheral.characteristic(WRITE_CHAR_FELICITA);
_read = peripheral.characteristic(READ_CHAR_FELICITA);
}
else{
Serial.println("unable to determine scale type");
Expand All @@ -132,17 +144,28 @@ bool AcaiaArduinoBLE::init(String mac){
Serial.println("subscribed!");
}

if(_write.writeValue(IDENTIFY, 20)){
Serial.println("identify write successful");
}else{
Serial.println("identify write failed");
return false;
}
if(_write.writeValue(NOTIFICATION_REQUEST,14)){
Serial.println("notification request write successful");
}else{
Serial.println("notification request write failed");
if(_type == OLD || _type == NEW){
if(_write.writeValue(IDENTIFY, sizeof(IDENTIFY))){
Serial.println("identify write successful");
}else{
Serial.println("identify write failed");
return false;
}
if(_write.writeValue(NOTIFICATION_REQUEST, sizeof(NOTIFICATION_REQUEST))){
Serial.println("notification request write successful");
}else{
Serial.println("notification request write failed");
return false;
}
}else if(_type == FELICITA){
// Wake the Felicita into a deterministic, known-good mode
// (weight + timer) instead of whatever IDENTIFY used to leave it in.
if(_write.writeValue(WEIGHT_TIMER_MODE_FELICITA, sizeof(WEIGHT_TIMER_MODE_FELICITA))){
Serial.println("felicita weight+timer mode set");
}else{
Serial.println("felicita mode set failed");
return false;
}
}
_connected = true;
_packetPeriod = 0;
Expand All @@ -154,8 +177,18 @@ bool AcaiaArduinoBLE::init(String mac){
return false;
}

// All command writes now use sizeof() so the byte count always matches the
// array. Felicita commands are single ASCII bytes; sending the right length
// (1) is essential -- the old code wrote 20/7 bytes from these 1-byte arrays,
// leaking adjacent memory to the scale as extra commands.
bool AcaiaArduinoBLE::tare(){
if(_write.writeValue((_type == GENERIC ? TARE_GENERIC : TARE_ACAIA), 6)){
bool ok;
switch(_type){
case GENERIC: ok = _write.writeValue(TARE_GENERIC, sizeof(TARE_GENERIC)); break;
case FELICITA: ok = _write.writeValue(TARE_FELICITA, sizeof(TARE_FELICITA)); break;
default: ok = _write.writeValue(TARE_ACAIA, sizeof(TARE_ACAIA)); break;
}
if(ok){
Serial.println("tare write successful");
return true;
}else{
Expand All @@ -166,8 +199,13 @@ bool AcaiaArduinoBLE::tare(){
}

bool AcaiaArduinoBLE::startTimer(){
if(_write.writeValue((_type == GENERIC ? START_TIMER_GENERIC : START_TIMER),
(_type == GENERIC ? 6 : 7))){
bool ok;
switch(_type){
case GENERIC: ok = _write.writeValue(START_TIMER_GENERIC, sizeof(START_TIMER_GENERIC)); break;
case FELICITA: ok = _write.writeValue(START_TIMER_FELICITA, sizeof(START_TIMER_FELICITA)); break;
default: ok = _write.writeValue(START_TIMER, sizeof(START_TIMER)); break;
}
if(ok){
Serial.println("start timer write successful");
return true;
}else{
Expand All @@ -178,8 +216,13 @@ bool AcaiaArduinoBLE::startTimer(){
}

bool AcaiaArduinoBLE::stopTimer(){
if(_write.writeValue((_type == GENERIC ? STOP_TIMER_GENERIC : STOP_TIMER),
(_type == GENERIC ? 6 : 7 ))){
bool ok;
switch(_type){
case GENERIC: ok = _write.writeValue(STOP_TIMER_GENERIC, sizeof(STOP_TIMER_GENERIC)); break;
case FELICITA: ok = _write.writeValue(STOP_TIMER_FELICITA, sizeof(STOP_TIMER_FELICITA)); break;
default: ok = _write.writeValue(STOP_TIMER, sizeof(STOP_TIMER)); break;
}
if(ok){
Serial.println("stop timer write successful");
return true;
}else{
Expand All @@ -190,8 +233,13 @@ bool AcaiaArduinoBLE::stopTimer(){
}

bool AcaiaArduinoBLE::resetTimer(){
if(_write.writeValue((_type == GENERIC ? RESET_TIMER_GENERIC : RESET_TIMER),
(_type == GENERIC ? 6 : 7 ))){
bool ok;
switch(_type){
case GENERIC: ok = _write.writeValue(RESET_TIMER_GENERIC, sizeof(RESET_TIMER_GENERIC)); break;
case FELICITA: ok = _write.writeValue(RESET_TIMER_FELICITA, sizeof(RESET_TIMER_FELICITA)); break;
default: ok = _write.writeValue(RESET_TIMER, sizeof(RESET_TIMER)); break;
}
if(ok){
Serial.println("reset timer write successful");
return true;
}else{
Expand Down Expand Up @@ -243,7 +291,8 @@ bool AcaiaArduinoBLE::newWeightAvailable(){
(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
(20 == l && GENERIC == _type) || //20 byte packets for generic scales
(18 == l && FELICITA == _type) //18 byte packets for Felicita Arc
){
_read.readValue(input, (l > 13) ? 13 : l); // readValue() seems to crash whenever l > weight packet (10, 13 or 18)

Expand Down Expand Up @@ -287,6 +336,19 @@ bool AcaiaArduinoBLE::newWeightAvailable(){
}
_currentWeight = _currentWeight / 100;
newWeightPacket = true;

}else if( FELICITA == _type && l == 18){
// Felicita Arc 18-byte ASCII packet (matches pyfelicita lib):
// byte 2 = sign ('-'/0x2D = negative)
// bytes 3..8 = six ASCII weight digits, value / 100 = grams
_currentWeight = (input[2] == 0x2D ? -1 : 1)
* ( (input[3] - 0x30) * 1000
+ (input[4] - 0x30) * 100
+ (input[5] - 0x30) * 10
+ (input[6] - 0x30) * 1
+ (input[7] - 0x30) * 0.1
+ (input[8] - 0x30) * 0.01 );
newWeightPacket = true;
}
if(newWeightPacket){
if(_lastPacket){
Expand All @@ -309,7 +371,8 @@ bool AcaiaArduinoBLE::isScaleName(String name){
|| nameShort == "LUNAR"
|| nameShort == "PEARL"
|| nameShort == "PROCH"
|| nameShort == "BOOKO";
|| nameShort == "BOOKO"
|| nameShort == "FELIC";
}

void AcaiaArduinoBLE::exploreService(BLEService service) {
Expand Down
14 changes: 9 additions & 5 deletions AcaiaArduinoBLE.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@
Released into the public domain.

Pio Baettig: Adding Felicita Arc support

A-TWJ: Fixing Felicita Arc bug (issue #15)

Known Bugs:
* Only supports Grams
*/
#ifndef AcaiaArduinoBLE_h
#define AcaiaArduinoBLE_h

#define LIBRARY_VERSION "3.3.0"
#define LIBRARY_VERSION "3.3.0-felicita-fix"
#define WRITE_CHAR_OLD_VERSION "2a80"
#define READ_CHAR_OLD_VERSION "2a80"
#define WRITE_CHAR_NEW_VERSION "49535343-8841-43f4-a8d4-ecbe34729bb3"
#define READ_CHAR_NEW_VERSION "49535343-1e4d-4bd9-ba61-23c647249616"
#define WRITE_CHAR_GENERIC "ff12"
#define READ_CHAR_GENERIC "ff11"
#define WRITE_CHAR_FELICITA "ffe1" // Felicita Arc: single ffe1 char (service ffe0) for both
#define READ_CHAR_FELICITA "ffe1" // notify (weight) and write (commands)
#define HEARTBEAT_PERIOD_MS 2750
#define MAX_PACKET_PERIOD_MS 5000

#include "Arduino.h"
#include <ArduinoBLE.h>

enum scale_type{
OLD, // Lunar (pre-2021)
NEW, // Lunar (2021), Pyxis
GENERIC // Felicita Arc, etc
OLD, // Lunar (pre-2021)
NEW, // Lunar (2021), Pyxis
GENERIC, // BooKoo Themis, Decent, etc (ff11/ff12, binary protocol)
FELICITA // Felicita Arc (ffe1, single-byte ASCII protocol)
};

class AcaiaArduinoBLE{
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Scale Compatibility:

☑ Pearl S

Felicita Arc (buggy, see bug report)
Felicita Arc (under-testing)

☑ Bookoo

Expand Down