initial commit

This commit is contained in:
Marius Unsel 2025-05-15 02:32:06 +02:00
commit 5e172c05ef
2 changed files with 516 additions and 0 deletions

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# SyncGrips Firmware
## BLE-enabled Pushup Parallettes
## Display Library Configuration for TTGO Dev Board
To config the [TFT eSPI library](https://github.com/Bodmer/TFT_eSPI), you have to edit `User_Setup_Select.h` in the library location.
Comment out `#include <User_Setup.h>` and uncomment `#include <User_Setups/Setup25_TTGO_T_Display.h>`.

506
SyncGrips/SyncGrips.ino Normal file
View File

@ -0,0 +1,506 @@
#define ESP32
//#define LEFT_DEVICE
#define DEBUG_SAMPLING
#include "Wire.h"
#include "HX711.h"
//Libraries for OLED Display
#include <TFT_eSPI.h>
#include <SPI.h>
#include "esp_timer.h"
#include <Preferences.h>
#ifdef CORE_DEBUG_LEVEL
#undef CORE_DEBUG_LEVEL
#endif
#define CORE_DEBUG_LEVEL 2
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "esp_log.h"
#include <math.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#ifndef TFT_DISPOFF
#define TFT_DISPOFF 0x28
#endif
#ifndef TFT_SLPIN
#define TFT_SLPIN 0x10
#endif
// PIN DEFINITIONS
#define TFT_MOSI 19
#define TFT_SCLK 18
#define TFT_CS 5
#define TFT_DC 16
#define TFT_RST 23
#define TFT_BL 4 // Display backlight control pin
#define ADC_EN 14 // ADC_EN is the ADC detection enable port
#define ADC_PIN 34
#define ADC_POWER 34
#ifdef DEBUG_SAMPLING
#define SAMPLE_INDICATOR 27
#define SAMPLE_INDICATOR_1 26
#endif
#ifdef LEFT_DEVICE
static const char* TAG = "SyncGrip L";
#define BUTTON_1 (gpio_num_t)35
#define BUTTON_2 (gpio_num_t)0
#else
static const char* TAG = "SyncGrip R";
#define BUTTON_1 (gpio_num_t)35
#define BUTTON_2 (gpio_num_t)0
#endif
TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library
Preferences preferences;
#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40 /* Greater the value, more the sensitivity */
#else //ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted) */
#define THRESHOLD 5000 /* Lower the value, more the sensitivity */
#endif
RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
#define SDA_1 21
#define SCL_1 22
// Module connection pins (Digital Pins)
#define CLK 22
#define DIO 21
enum DeviceState {UNCONNECTED, CONNECTED, CALIBRATE};
enum DeviceState state = UNCONNECTED;
int old_btn_state =1 ;
int btn_state = 0;
float reading;
float maxReading;
uint64_t nLoopRuns = 0;
uint64_t nSamples = 0 ;
int missedSamples =0;
HX711 scale;
uint32_t scaleOffset;
float scaleScaler;
// TIME SECTION
int64_t time_since_boot;
esp_timer_handle_t button_timer;
esp_timer_handle_t sampling_timer;
static void button_timer_callback(void* arg);
static void sampling_timer_callback(void* arg);
// BLE SECTION
BLEServer *pServer = NULL;
BLECharacteristic *sensor_val_characteristic = NULL;
BLECharacteristic *bat_val_characteristic = NULL;
BLECharacteristic *sync_characteristic = NULL;
BLECharacteristic *calibrate_characteristic = NULL;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "00567f83-72b0-4f28-b7ef-1fce18766da6"
#define SENSOR_VAL_CHARACTERISTIC_UUID "082c9546-6a26-4230-a7b2-8d13fa96da55"
#define BAT_VAL_CHARACTERISTIC_UUID "53aec279-cf8f-423f-bf13-38f778696fa0"
#define SYNC_CHARACTERISTIC_UUID "f1a13d24-57f0-4264-96bf-2c97c8a37561"
#define CALIBRATE_CHARACTERISTIC_UUID "89cc5093-096a-4d3e-b7db-18a979bb81fb"
// TIMERS
void initButtonTimer(){
const esp_timer_create_args_t button_timer_args = {
.callback = &button_timer_callback,
/* name is optional, but may help identify the timer when debugging */
.name = "buttonpoll"
};
ESP_ERROR_CHECK(esp_timer_create(&button_timer_args, &button_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(button_timer, 100000));//every 100ms
}
void initSamplingTimer(){
const esp_timer_create_args_t sampling_timer_args = {
.callback = &sampling_timer_callback,
.name = "sampling"
};
ESP_ERROR_CHECK(esp_timer_create(&sampling_timer_args, &sampling_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(sampling_timer, 105000));//every 105ms
}
// BLE CALLBACKS
class MyServerCallbacks : public BLEServerCallbacks
{
void onConnect(BLEServer *pServer)
{
Serial.println("Connected");
state = CONNECTED;
};
void onDisconnect(BLEServer *pServer)
{
state = UNCONNECTED;
Serial.println("Disconnected, starting advertising again...");
// Start advertising
pServer->getAdvertising()->start();
}
};
float measureBattery(){
// measures with 12 bit ADC on a voltage divider with 3.3V power supply
uint16_t readVal = analogRead(ADC_POWER);
float vBat = 2.*3.3*((0.0+readVal) / 4096.0);
ESP_LOGE(TAG, "Measure BAT: %d %fV", readVal, vBat);
return vBat;
}
int batteryPercent(float vBat){
return int(100*(vBat-3.3)/(4.2-3.3));
}
bool screenon = true;
void toggleScreen(){
screenon = !screenon;
if (screenon){
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
}
else {
digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON);
}
}
void tare(){
state = CALIBRATE;
tft.fillScreen(TFT_BLACK);
tft.drawString("TARE", 120,32);
scale.tare(20);
uint32_t offset = scale.get_offset();
preferences.begin("calibration", false); //false for r/w mode instead of just r
preferences.putULong("offset", offset);
preferences.end();
tft.fillScreen(TFT_BLACK);
tft.drawString("DONE", 120,32);
delay(500);
state = CONNECTED;
Serial.println("DONE TARE");
}
void calibrate(int weightGrams){
state = CALIBRATE;
tft.fillScreen(TFT_BLACK);
tft.drawString("calibrate", 120,32);
scale.calibrate_scale(weightGrams, 20);
float scaler = scale.get_scale();
preferences.begin("calibration", false); //false for r/w mode instead of just r
preferences.putFloat("scale", scaler);
preferences.end();
tft.fillScreen(TFT_BLACK);
tft.drawString("DONE", 120,32);
delay(500);
state = CONNECTED;
}
class BatteryCharacteristicCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
float batVal = measureBattery();
int batPercent = batteryPercent(batVal);
bat_val_characteristic->setValue(String(batPercent).c_str());
bat_val_characteristic->notify();
}
};
class SyncCharacteristicCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
// TODO handle SYNC event
std::string value = pCharacteristic->getValue();
uint32_t int_val = (uint32_t) value.c_str();
state = CALIBRATE;
ESP_ERROR_CHECK(esp_timer_stop(sampling_timer));
#ifdef LEFT_DEVICE
delay(1000);
#else
delay(532); // measured 32ms offset
#endif
tft.fillScreen(TFT_RED);
delay(200);
nSamples = 0;
missedSamples = 0;
state = CONNECTED;
ESP_ERROR_CHECK(esp_timer_start_periodic(sampling_timer, 105000));//every 105ms
}
};
class CalibrateCharacteristicCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
state = CALIBRATE;
Serial.println("SHOW ME THE CALIBRATE");
tft.fillScreen(TFT_RED);
delay(200);
int nGrams = atoi(pCharacteristic->getValue().c_str());
if(nGrams == 0) {
tare();
}
if(nGrams > 0) {
calibrate(nGrams);
}
state=CONNECTED;
}
};
class CharacteristicsCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
uint32_t int_val = (uint32_t) value.c_str();
Serial.println("some value arrived");
tft.fillScreen(TFT_RED);
delay(2000);
}
};
void drawReading(float reading, float maxReading) {
String out = String(reading<0?0:reading)+"kg";
String out1 = String(maxReading)+"kg";
tft.fillScreen(TFT_BLACK);
tft.drawString(out, 120, 32);
tft.drawString(out1, 120, 84);
}
void drawDebugReading(float reading, int missedCount) {
String out = String(reading<0?0:reading)+"kg";
String out1 = String(missedCount)+" "+ (state==UNCONNECTED ? "NC": "C");
tft.fillScreen(TFT_BLACK);
tft.drawString(out, 120, 32);
tft.drawString(out1, 120, 84);
}
void initScale(){
scale.begin(DIO, CLK);
preferences.begin("calibration", false); //false for r/w mode instead of just r
float scaler = preferences.getFloat("scale", -26.326582);
uint32_t offset = preferences.getULong("offset", 4294856599);
preferences.end();
scale.set_offset(offset);
scale.set_scale(scaler);
}
void initTFT(){
tft.init();
tft.setTextSize(5);
if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. TFT_BACKLIGHT_ON has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
}
#ifdef LEFT_DEVICE
tft.setRotation(1);
#else
tft.setRotation(3);
#endif
tft.fillScreen(TFT_RED);
delay(20);
tft.fillScreen(TFT_BLUE);
delay(20);
tft.fillScreen(TFT_GREEN);
delay(20);
tft.fillScreen(TFT_BLACK);
tft.setTextDatum(MC_DATUM);
tft.drawString("Hello!", tft.width() / 2, tft.height() / 2);
}
int64_t currentReadingTimestamp;
int64_t readingStartTimestamp;
void setup(){
Serial.begin(115200);
initTFT();
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
// TOGGLE Pin for sampling timing mesurements
#ifdef DEBUG_SAMPLING
pinMode(SAMPLE_INDICATOR, OUTPUT);
pinMode(SAMPLE_INDICATOR_1, OUTPUT);
#endif
initScale();
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
BLEDevice::init(TAG);
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
delay(100);
uint32_t props = BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE;
// Create BLE Characteristics
sensor_val_characteristic = pService->createCharacteristic(
SENSOR_VAL_CHARACTERISTIC_UUID,
props);
bat_val_characteristic = pService->createCharacteristic(
BAT_VAL_CHARACTERISTIC_UUID,
props);
sync_characteristic = pService->createCharacteristic(
SYNC_CHARACTERISTIC_UUID,
props);
calibrate_characteristic = pService->createCharacteristic(
CALIBRATE_CHARACTERISTIC_UUID,
props);
sensor_val_characteristic->setValue("0.0 0");
float vBat = measureBattery();
int batPercent = batteryPercent(vBat);
/* Serial.println("VBAT: "+ String(vBat)+", "+String(batPercent)+"%"); */
bat_val_characteristic->setValue(String(batPercent).c_str());
//#ifdef LEFT_DEVICE
//bat_val_characteristic->setValue("7");
//#else
//bat_val_characteristic->setValue("99");
//#endif
bat_val_characteristic->setCallbacks(new CharacteristicsCallbacks());
sensor_val_characteristic->setCallbacks(new CharacteristicsCallbacks());
sync_characteristic->setCallbacks(new SyncCharacteristicCallbacks());
calibrate_characteristic->setCallbacks(new CalibrateCharacteristicCallbacks());
// Start the BLE service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting for a client connection to notify...");
tft.fillScreen(TFT_BLACK);
//esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0);
esp_sleep_enable_ext0_wakeup(BUTTON_1, 0);
initButtonTimer();
initSamplingTimer();
time_since_boot = esp_timer_get_time();
readingStartTimestamp = esp_timer_get_time();
ESP_LOGI(TAG, "startup finished, time since boot: %lld us", time_since_boot);
}
void loop() {
nLoopRuns++;
//time_since_boot = esp_timer_get_time();
//ESP_LOGI(TAG, "loop called, time since boot: %lld us", time_since_boot);
float vBat = measureBattery();
int batPercent = batteryPercent(vBat);
//Serial.println("VBAT: "+ String(vBat)+", "+String(batPercent)+"%");
bat_val_characteristic->setValue(String(batPercent).c_str());
bat_val_characteristic->notify();
delay(20000);
}
static void sampling_timer_callback(void* arg){
digitalWrite(SAMPLE_INDICATOR, HIGH);
int64_t localTimestamp;
char valuesOut[20];
if (state == CALIBRATE) {
return;
}
nSamples = (nSamples + 1) % 3000;
scale.wait_ready();
if (scale.is_ready()) {
digitalWrite(SAMPLE_INDICATOR_1, HIGH);
reading = scale.get_units()/1000;
reading = reading <0 ? 0:reading;
//currentReadingTimestamp = esp_timer_get_time();
//localTimestamp = currentReadingTimestamp - readingStartTimestamp;
sprintf(valuesOut, "%.2f %d", reading, nSamples);// localTimestamp);
// sending reading via BLE
if(state == CONNECTED){
sensor_val_characteristic->setValue(valuesOut);
#ifdef LEFT_DEVICE
delay(10);
#endif
sensor_val_characteristic->notify();
}
if (reading > maxReading){ maxReading=reading;}
digitalWrite(SAMPLE_INDICATOR_1, LOW);
if(screenon){
//drawReading(reading, maxReading);
drawDebugReading(reading, missedSamples);
}
/* ESP_LOGI(TAG, "scale was ready, time since last reading: %lld us", localTimestamp); */
} else {
missedSamples++;
drawDebugReading(reading, missedSamples);
}
digitalWrite(SAMPLE_INDICATOR, LOW);
}
static void button_timer_callback(void* arg){
btn_state = digitalRead(BUTTON_1);
if(btn_state && !old_btn_state){
ESP_ERROR_CHECK(esp_timer_stop(button_timer));
ESP_ERROR_CHECK(esp_timer_delete(button_timer));
ESP_ERROR_CHECK(esp_timer_stop(sampling_timer));
ESP_ERROR_CHECK(esp_timer_delete(sampling_timer));
Serial.println("pressed sleep button");
maxReading=0;
tft.fillScreen(TFT_BLACK);
tft.setTextDatum(MC_DATUM);
tft.drawString("Bye...", tft.width() / 2, tft.height() / 2);
delay(500);
esp_deep_sleep_start();
//toggleScreen();
}
old_btn_state = btn_state;
//Serial.println(nLoopRuns);
// nLoopRuns = 0;
}