diff --git a/README.md b/README.md index 892a804..10b1995 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ bool is24Hour(); String getAddress(); void setBattery(uint8_t level, bool charging = false); bool isCameraReady(); +void syncRequest(); // notifications int getNotificationCount(); @@ -57,6 +58,7 @@ String getWeatherCity(); String getWeatherTime(); Weather getWeatherAt(int index); HourlyForecast getForecastHour(int hour); +WeatherLocation getWeatherLocation(); // extras RemoteTouch getTouch(); diff --git a/examples/watch/watch.ino b/examples/watch/watch.ino index 75ed852..32c04fd 100644 --- a/examples/watch/watch.ino +++ b/examples/watch/watch.ino @@ -287,7 +287,19 @@ void configCallback(Config config, uint32_t a, uint32_t b) { Serial.print("City name: "); String city = watch.getWeatherCity(); // - Serial.print(city); + Serial.println(city); + + WeatherLocation loc = watch.getWeatherLocation(); + Serial.print("City: "); + Serial.println(loc.city); + Serial.print("Region: "); + Serial.println(loc.region); + Serial.print("Country: "); + Serial.println(loc.country); + Serial.print("Coordinates -> Lat: "); + Serial.print(loc.latitude, 6); + Serial.print("\tLon: "); + Serial.println(loc.longitude, 6); } Serial.println(); break; diff --git a/keywords.txt b/keywords.txt index 69eddfd..6aeb25f 100644 --- a/keywords.txt +++ b/keywords.txt @@ -14,6 +14,7 @@ is24Hour KEYWORD2 getAddress KEYWORD2 setBattery KEYWORD2 isCameraReady KEYWORD2 +syncRequest KEYWORD2 getNotificationCount KEYWORD2 getNotificationAt KEYWORD2 clearNotifications KEYWORD2 @@ -22,13 +23,13 @@ getWeatherCity KEYWORD2 getWeatherTime KEYWORD2 getWeatherAt KEYWORD2 getForecastHour KEYWORD2 +getWeatherLocation KEYWORD2 getTouch KEYWORD2 getQrAt KEYWORD2 setQr KEYWORD2 getAlarm KEYWORD2 setAlarm KEYWORD2 isAlarmActive KEYWORD2 -isAlarmActive KEYWORD2 isAnyAlarmActive KEYWORD2 sendCommand KEYWORD2 musicControl KEYWORD2 @@ -73,6 +74,7 @@ Control LITERAL1 SleepType LITERAL1 Notification LITERAL1 Weather LITERAL1 +WeatherLocation LITERAL1 HourlyForecast LITERAL1 ChronosTimer LITERAL1 ChronosData LITERAL1 @@ -122,6 +124,7 @@ CF_QR LITERAL1 CF_NAV_DATA LITERAL1 CF_NAV_ICON LITERAL1 CF_CONTACT LITERAL1 +CF_SYNCED LITERAL1 HR_STEPS_RECORDS LITERAL1 HR_SLEEP_RECORDS LITERAL1 @@ -130,3 +133,38 @@ HR_BLOOD_OXYGEN_MEASURE LITERAL1 HR_BLOOD_PRESSURE_MEASURE LITERAL1 HR_MEASURE_ALL LITERAL1 +CS_0x0_000_CFF LITERAL1 +CS_240x240_130_STF LITERAL1 +CS_240x240_130_STT LITERAL1 +CS_80x160_096_RTF LITERAL1 +CS_80x160_096_RTT LITERAL1 +CS_135x240_114_RTF LITERAL1 +CS_135x240_114_RTT LITERAL1 +CS_240x240_128_CTF LITERAL1 +CS_240x240_128_CTT LITERAL1 +CS_240x288_157_RTF LITERAL1 +CS_240x288_157_RTT LITERAL1 +CS_240x283_172_RTF LITERAL1 +CS_240x283_172_RTT LITERAL1 +CS_360x360_130_CTF LITERAL1 +CS_360x360_130_CTT LITERAL1 +CS_320x380_177_RTF LITERAL1 +CS_320x380_177_RTT LITERAL1 +CS_320x385_175_RTF LITERAL1 +CS_320x385_175_RTT LITERAL1 +CS_320x360_160_RTF LITERAL1 +CS_320x360_160_RTT LITERAL1 +CS_240x296_191_RTF LITERAL1 +CS_240x296_191_RTT LITERAL1 +CS_412x412_145_CTF LITERAL1 +CS_412x412_145_CTT LITERAL1 +CS_410x494_200_RTF LITERAL1 +CS_410x494_200_RTT LITERAL1 +CS_466x466_143_CTF LITERAL1 +CS_466x466_143_CTT LITERAL1 +CF_WATCHY_200x200 LITERAL1 +CF_ESP32_240x240 LITERAL1 +CF_ESP32_466x466 LITERAL1 +CF_WEBSCREEN_536x240 LITERAL1 +CF_WAVESHARE_410x502 LITERAL1 +CF_ZSWATCH_240x240 LITERAL1 diff --git a/library.json b/library.json index 80b928c..7f042bb 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ChronosESP32", - "version": "1.8.2", + "version": "1.9.0", "keywords": "Arduino, ESP32, Time, BLE, Watch", "description": "A library for ESP32 to interface with Chronos app over BLE", "repository": diff --git a/library.properties b/library.properties index 81d0e10..a5106f5 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ChronosESP32 -version=1.8.2 +version=1.9.0 author=fbiego maintainer=fbiego sentence=Setup your ESP32 as a smartwatch and connect to Chronos app over BLE. diff --git a/platformio.ini b/platformio.ini index 77209d5..5ea8799 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,6 +24,7 @@ src_dir = examples/watch platform = espressif32 framework = arduino extra_scripts = post:scripts/copy.py +monitor_speed = 115200 lib_deps = ; use src folder as library diff --git a/scripts/keywords.py b/scripts/keywords.py index 994193c..c2a4b08 100644 --- a/scripts/keywords.py +++ b/scripts/keywords.py @@ -62,7 +62,7 @@ def extract_keywords(header_file_path, library_name="ChronosESP32", output_file= # Enum values enum_blocks = re.findall(r"enum\s+[a-zA-Z_]\w*\s*{([^}]+)}", full_content, re.DOTALL) for block in enum_blocks: - enum_values = re.findall(r"\b([A-Z_][A-Z0-9_]*)\b", block) + enum_values = re.findall(r"\b([A-Z_][A-Za-z0-9_]*)\b", block) for val in enum_values: output_lines.append(f"{val}\tLITERAL1") output_lines.append("") diff --git a/src/ChronosESP32.cpp b/src/ChronosESP32.cpp index a5b1fe6..0f7053f 100644 --- a/src/ChronosESP32.cpp +++ b/src/ChronosESP32.cpp @@ -393,6 +393,14 @@ HourlyForecast ChronosESP32::getForecastHour(int hour) return _hourlyForecast[hour % FORECAST_SIZE]; } +/*! + @brief return the weather location data +*/ +WeatherLocation ChronosESP32::getWeatherLocation() +{ + return _weatherLocation; +} + /*! @brief get the alarm at the index @param index @@ -775,6 +783,15 @@ void ChronosESP32::sendBattery() sendCommand(batCmd, 8); } +/*! + @brief request the app to sync settings +*/ +void ChronosESP32::syncRequest() +{ + uint8_t syncCmd[] = {0xAB, 0x00, 0x03, 0xFE, 0x23, 0x80}; + sendCommand(syncCmd, 6); +} + /*! @brief request the battery level of the phone */ @@ -1384,6 +1401,15 @@ void ChronosESP32::dataReceived() switch (_incomingData.data[4]) { + case 0x20: + if (_incomingData.data[3] == 0xFE) + { + if (configurationReceivedCallback != nullptr) + { + configurationReceivedCallback(CF_SYNCED, 0, 0); + } + } + break; case 0x23: if (configurationReceivedCallback != nullptr) { @@ -1680,11 +1706,16 @@ void ChronosESP32::dataReceived() break; case 0x93: + if (configurationReceivedCallback != nullptr) + { + configurationReceivedCallback(CF_TIME, 0, 0); + } + this->setTime(_incomingData.data[13], _incomingData.data[12], _incomingData.data[11], _incomingData.data[10], _incomingData.data[9], _incomingData.data[7] * 256 + _incomingData.data[8]); if (configurationReceivedCallback != nullptr) { - configurationReceivedCallback(CF_TIME, 0, 0); + configurationReceivedCallback(CF_TIME, 1, 0); } break; case 0x9C: @@ -1906,8 +1937,10 @@ void ChronosESP32::dataReceived() } else if (_incomingData.data[0] == 0xEA) { - if (_incomingData.data[4] == 0x7E) + switch (_incomingData.data[4]) { + case 0x7E: + /* code */ switch (_incomingData.data[5]) { case 0x01: @@ -1948,7 +1981,50 @@ void ChronosESP32::dataReceived() } } break; + } /* END switch (_incomingData.data[5]) */ + break; + + case 0x7F: + if (_incomingData.data[3] == 0xFE) + { + uint8_t payloadLen = _incomingData.data[6]; + const uint8_t *payload = &_incomingData.data[7]; + + // Read coordinates (Little Endian) + float latitude; + float longitude; + memcpy(&latitude, payload, 4); + memcpy(&longitude, payload + 4, 4); + + // Move pointer past coordinates + int index = 8; + + // Read city (null-terminated) + String city = ""; + while (index < payloadLen && payload[index] != 0x00) + city += (char)payload[index++]; + index++; // skip null + + // Read region (null-terminated) + String region = ""; + while (index < payloadLen && payload[index] != 0x00) + region += (char)payload[index++]; + index++; // skip null + + // Remaining bytes = country + String country = ""; + while (index < payloadLen) + country += (char)payload[index++]; + + // Assign to struct + _weatherLocation.city = city; + _weatherLocation.region = region; + _weatherLocation.country = country; + _weatherLocation.latitude = latitude; + _weatherLocation.longitude = longitude; } + + break; } } } diff --git a/src/ChronosESP32.h b/src/ChronosESP32.h index 93f4512..b1bc624 100644 --- a/src/ChronosESP32.h +++ b/src/ChronosESP32.h @@ -38,8 +38,8 @@ #include #define CHRONOSESP_VERSION_MAJOR 1 -#define CHRONOSESP_VERSION_MINOR 8 -#define CHRONOSESP_VERSION_PATCH 2 +#define CHRONOSESP_VERSION_MINOR 9 +#define CHRONOSESP_VERSION_PATCH 0 #define CHRONOSESP_VERSION F(CHRONOSESP_VERSION_MAJOR "." CHRONOSESP_VERSION_MINOR "." CHRONOSESP_VERSION_PATCH) @@ -97,6 +97,15 @@ struct Weather int uv; }; +struct WeatherLocation +{ + String city; + String region; + String country; + float latitude; + float longitude; +}; + struct HourlyForecast { int day; // day of forecast @@ -177,7 +186,7 @@ struct DateTime enum Config { - CF_TIME = 0, // time - + CF_TIME = 0, // time - (a 0 = before, 1 = after setting) CF_RTW, // raise to wake - CF_HR24, // 24 hour mode - CF_LANG, // watch language - @@ -200,6 +209,7 @@ enum Config CF_NAV_DATA, // navigation data received CF_NAV_ICON, // navigation icon received CF_CONTACT, // contacts data received + CF_SYNCED, // data sync completed (esp32 can go to sleep if needed) }; enum HealthRequest @@ -250,7 +260,16 @@ enum ChronosScreen CS_410x494_200_RTF = 25, // 410x494, 2.0 inches, Rectangular, True, False CS_410x494_200_RTT = 32, // 410x494, 2.0 inches, Rectangular, True, True CS_466x466_143_CTF = 33, // 466x466, 1.43 inches, Round, True, False - CS_466x466_143_CTT = 34 // 466x466, 1.43 inches, Round, True, True + CS_466x466_143_CTT = 34, // 466x466, 1.43 inches, Round, True, True + + // extended configurations for open source projects, used as an identifier for Chronos App + CF_WATCHY_200x200 = 0x80, // Watchy 200x200 (E_PAPER) + CF_ESP32_240x240 = 0x81, // Generic ESP32 240x240 + CF_ESP32_466x466 = 0x82, // Generic ESP32 466x466 (AMOLED) + CF_WEBSCREEN_536x240 = 0x83, // Webscreen 536x240 (AMOLED) + CF_WAVESHARE_410x502 = 0x84, // Waveshare 410x502 (AMOLED) + CF_ZSWATCH_240x240 = 0x85, // ZSWatch 240x240 + CF_VIEWE_28_240x320 = 0x86, // Viewe ESP32 240x320 }; class ChronosESP32 : public BLEServerCallbacks, public BLECharacteristicCallbacks, public ESP32Time @@ -259,13 +278,13 @@ class ChronosESP32 : public BLEServerCallbacks, public BLECharacteristicCallback public: // library ChronosESP32(); - ChronosESP32(String name, ChronosScreen screen = CS_240x240_128_CTF); // set the BLE name - void begin(); // initializes BLE server - void stop(bool clearAll = true); // stop the BLE server - void loop(); // handles routine functions - bool isRunning(); // check whether BLE server is inited and running - void setName(String name); // set the BLE name (call before begin) - void setScreen(ChronosScreen screen); // set the screen config (call before begin) + ChronosESP32(String name, ChronosScreen screen = CF_ESP32_240x240); // set the BLE name + void begin(); // initializes BLE server + void stop(bool clearAll = true); // stop the BLE server + void loop(); // handles routine functions + bool isRunning(); // check whether BLE server is inited and running + void setName(String name); // set the BLE name (call before begin) + void setScreen(ChronosScreen screen); // set the screen config (call before begin) void setChunkedTransfer(bool chunked); bool isSubscribed(); @@ -276,6 +295,7 @@ class ChronosESP32 : public BLEServerCallbacks, public BLECharacteristicCallback String getAddress(); void setBattery(uint8_t level, bool charging = false); bool isCameraReady(); + void syncRequest(); // notifications int getNotificationCount(); @@ -288,6 +308,7 @@ class ChronosESP32 : public BLEServerCallbacks, public BLECharacteristicCallback String getWeatherTime(); Weather getWeatherAt(int index); HourlyForecast getForecastHour(int hour); + WeatherLocation getWeatherLocation(); // extras RemoteTouch getTouch(); @@ -395,6 +416,7 @@ class ChronosESP32 : public BLEServerCallbacks, public BLECharacteristicCallback String _weatherCity; String _weatherTime; int _weatherSize; + WeatherLocation _weatherLocation; HourlyForecast _hourlyForecast[FORECAST_SIZE];