Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Anrijs committed Apr 18, 2021
0 parents commit 195972a
Show file tree
Hide file tree
Showing 6 changed files with 490 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Aranet4 ESP32 client

This library allows you to read data from Aranet4 devices using Bluetooth.
76 changes: 76 additions & 0 deletions examples/BasicRead/BasicRead.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* This simple example demonstrates how to connect to Aranet4
* device and read sensor data
*
* Name: BasicRead.ino
* Created: 2021-04-18
* Author: Anrijs Jargans <anrijs@anrijs.lv>
* Url: https://github.com/Anrijs/Aranet4-ESP32
*/

#include "Aranet4.h"

// Create custom callback to allow PIN code input.
// In this example, when PIN is requested, you must enter it in serial console.
class MyAranet4Callbacks: public Aranet4Callbacks {
uint32_t onPinRequested() {
Serial.println("PIN Requested. Enter PIN in serial console.");
while(Serial.available() == 0)
vTaskDelay(500 / portTICK_PERIOD_MS);

return Serial.readString().toInt();
}

void onConnected() {
Serial.println("Device conencted");
}

void onFailed(uint8_t code) {
Serial.print("Connection failed: ");
switch (code) {
case AR4_ERR_NOT_CONNECTED: Serial.println("not connected"); break;
case AR4_ERR_UNAUTHORIZED: Serial.println("unauthorized"); break;
default: Serial.println("unknown)"); break;
}
}

void onDisconnected() {
Serial.println("Aranet4 disconencted");
}
};

void setup() {
Serial.begin(115200);
Serial.println("Init");

// Address can be string or byte array
// uint8_t addr[] = {0xc00 0x01, 0x02, 0x03, 0x04, 0x05 };
String addr = "00:01:02:03:04:05"; // Put your Aranet4 MAC address here

Aranet4 ar4;
ar4.init(new MyAranet4Callbacks());

Serial.println("Connecting...");
if (ar4.connect(addr) == AR4_OK) {
AranetData data = ar4.getCurrentReadings();

if (ar4.getStatus() == AR4_OK) {
Serial.println("Aranet4 read OK");
Serial.printf("CO2: %i ppm\n", data.co2);
Serial.printf("Temperature: %.2f C\n", data.temperature / 20.0);
Serial.printf("Pressure: %.1f C\n", data.pressure / 10.0);
Serial.printf("Humidity: %i %%\n", data.humidity);
Serial.printf("Battery: %i %%\n", data.battery);
Serial.printf("Interval: %i s\n", data.interval);
Serial.printf("Ago: %i s\n", data.ago);
} else {
Serial.printf("Aranet4 read failed: (%i)\n", ar4.getStatus());
}
}

ar4.disconnect();
}

void loop() {

}
40 changes: 40 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#######################################
# Syntax Coloring Map For Aranet4
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

Aranet4 KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

readData KEYWORD2
isPaired KEYWORD2

#######################################
# Instances (KEYWORD2)
#######################################

co2 KEYWORD2
temperature KEYWORD2
pressure KEYWORD2
humidity KEYWORD2
battery KEYWORD2
interval KEYWORD2
ago KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################

AR4_OK LITERAL1
AR4_FAIL LITERAL1
AR4_ERR_NO_GATT_SERVICE LITERAL1
AR4_ERR_NO_GATT_CHAR LITERAL1
AR4_ERR_NO_CLIENT LITERAL1
AR4_ERR_NOT_CONNECTED LITERAL1
AR4_ERR_UNAUTHORIZED LITERAL1
9 changes: 9 additions & 0 deletions library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name=Aranet4
version=1.0.0
author=Anrijs Jargans
maintainer=Anrijs Jargans <anrijs@anrijs.lv>
sentence= Aranet4 communication library for ESP32
paragraph=Allows to pair with Aranet4 device and read measurements
category=Communication
url=https://github.com/Anrijs/Aranet4-ESP32
architectures=esp32
227 changes: 227 additions & 0 deletions src/Aranet4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Name: Aranet4.cpp
* Created: 2021-04-17
* Author: Anrijs Jargans <anrijs@anrijs.lv>
* Url: https://github.com/Anrijs/Aranet4-ESP32
*/

#include "aranet4.h"
#include "Arduino.h"

/**
* @brief Initialize ESP32 bluetooth device and security profile
* @param [in] cllbacks Pointer to Aranet4Callbacks class callback
*/
void Aranet4::init(Aranet4Callbacks* callbacks) {
aranetCallbacks = callbacks;

// Set up bluetooth device and security
BLEDevice::init("");
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
BLEDevice::setSecurityCallbacks(aranetCallbacks);

BLESecurity *pSecurity = new BLESecurity();
pSecurity->setKeySize();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
pSecurity->setCapability(ESP_IO_CAP_IN);
pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
}

/**
* @brief Check if device is paired
* @param [in] addr Address of bluetooth device
* @return True if device is paired
*/
bool Aranet4::isPaired(esp_bd_addr_t addr) {
int count = esp_ble_get_bond_device_num();

esp_ble_bond_dev_t* devList = (esp_ble_bond_dev_t*) malloc(count * sizeof(esp_ble_bond_dev_t));
esp_err_t status = esp_ble_get_bond_device_list(&count, devList);

if (status != ESP_OK) {
Serial.println("Aranet4: Failed to get bonded device list");
return false;
}

for (int devId=0; devId<count; devId++) {
esp_ble_bond_dev_t paired = devList[devId];
if (memcmp(addr, paired.bd_addr, ESP_BD_ADDR_LEN) == 0) return true;
}

return false;
}

/**
* @brief Connect to Aranet4 device
* @param [in] addr Address of bluetooth device
* @return Connection status code (AR4_CONN_*)
*/
ar4_err_t Aranet4::connect(esp_bd_addr_t addr) {
pClient = BLEDevice::createClient();

bool stat = pClient->connect(addr, BLE_ADDR_TYPE_RANDOM);

if (!stat) {
aranetCallbacks->onFailed(AR4_ERR_NOT_CONNECTED);
return AR4_ERR_NOT_CONNECTED;
}

// Wait for auth callback
while(!aranetCallbacks->isConnected()) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}

if (aranetCallbacks->isAuthenticated()) {
aranetCallbacks->onConnected();
return AR4_OK;
}

aranetCallbacks->onFailed(AR4_ERR_UNAUTHORIZED);
return AR4_ERR_UNAUTHORIZED;
}

/**
* @brief Connect to Aranet4 device
* @param [in] addr Address of bluetooth device
* @return Connection status code (AR4_CONN_*)
*/
ar4_err_t Aranet4::connect(String addr) {
BLEAddress bleAddr = BLEAddress(addr.c_str());
esp_bd_addr_t* native = bleAddr.getNative();

return connect(*bleAddr.getNative());
}

/**
* @brief Disconnects from bluetooth device
*/
void Aranet4::disconnect() {
if (pClient != nullptr) {
// Without delay, there could be crash
vTaskDelay(500 / portTICK_PERIOD_MS);
pClient->disconnect();
}

aranetCallbacks->onDisconnected();
}

/**
* @brief Current readings from Aranet4
*/
AranetData Aranet4::getCurrentReadings() {
AranetData data;
uint16_t len = sizeof(AranetData);
status = getValue(UUID_Aranet4, UUID_Aranet4_CurrentReadingsDet, (uint8_t*) &data, &len);

return data;
}

/**
* @brief Seconds since last Aranet4 measurement
*/
uint16_t Aranet4::getSecondsSinceUpdate() {
return getU16Value(UUID_Aranet4, UUID_Aranet4_SecondsSinceUpdate);
}

/**
* @brief Total readings stored in Aranet4 memory
*/
uint16_t Aranet4::getTotalReadings() {
return getU16Value(UUID_Aranet4, UUID_Aranet4_TotalReadings);
}

/**
* @brief Aranet4 measurement intervals
*/
uint16_t Aranet4::getInterval() {
return getU16Value(UUID_Aranet4, UUID_Aranet4_Interval);
}

/**
* @brief Aranet4 device name
*/
String Aranet4::getName() {
return getStringValue(UUID_Generic, UUID_Generic_DeviceName);
}

/**
* @brief Aranet4 software version
*/
String Aranet4::getVersion() {
return getStringValue(UUID_Common, UUID_Common_SwRev);
}

/**
* @brief Status code of last action
*/
ar4_err_t Aranet4::getStatus() {
return status;
}

/**
* @brief Reads raw data from Aranet4
* @param [in] serviceUuid GATT Service UUID to read
* @param [in] charUuid GATT Char UUID to read
* @param [out] data Pointer to where received data will be stored
* @param [in|out] Size of data on input, received data size on output (truncated if larger than input)
* @return Read status code (AR4_READ_*)
*/
ar4_err_t Aranet4::getValue(BLEUUID serviceUuid, BLEUUID charUuid, uint8_t* data, uint16_t* len) {
if (pClient == nullptr) return AR4_ERR_NO_CLIENT;

if (!aranetCallbacks->isAuthenticated()) return AR4_ERR_UNAUTHORIZED;
if (!aranetCallbacks->isConnected()) return AR4_ERR_NOT_CONNECTED;

BLERemoteService* pRemoteService = pClient->getService(serviceUuid);
if (pRemoteService == nullptr) {
return AR4_ERR_NO_GATT_SERVICE;
}

BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUuid);
if (pRemoteCharacteristic == nullptr) {
return AR4_ERR_NO_GATT_CHAR;
}

// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
std::string str = pRemoteCharacteristic->readValue();
if (str.length() < *len) *len = str.length();
memcpy(data, str.c_str(), *len);
return AR4_OK;
}

return AR4_FAIL;
}

/**
* @brief Reads string value from Aranet4
* @param [in] serviceUuid GATT Service UUID to read
* @param [in] charUuid GATT Char UUID to read
* @return String value
*/
String Aranet4::getStringValue(BLEUUID serviceUuid, BLEUUID charUuid) {
uint8_t buf[33];
uint16_t len = 32;
status = getValue(serviceUuid, charUuid, buf, &len);
buf[len] = 0; // trerminate string
return String((char *) buf);
}

/**
* @brief Reads u16 value from Aranet4
* @param [in] serviceUuid GATT Service UUID to read
* @param [in] charUuid GATT Char UUID to read
* @return u16 value
*/
uint16_t Aranet4::getU16Value(BLEUUID serviceUuid, BLEUUID charUuid) {
uint16_t val = 0;
uint16_t len = 2;
status = getValue(serviceUuid, charUuid, (uint8_t *) &val, &len);

if (len == 2) {
return val;
}

status = AR4_FAIL;
return 0;
}
Loading

0 comments on commit 195972a

Please sign in to comment.