diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..02c07cc
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,28 @@
+[build]
+target = "riscv32imc-esp-espidf"
+
+[target.xtensa-esp32-espidf]
+linker = "ldproxy"
+
+[target.xtensa-esp32s2-espidf]
+linker = "ldproxy"
+
+[target.xtensa-esp32s3-espidf]
+linker = "ldproxy"
+
+[target.riscv32imc-esp-espidf]
+linker = "ldproxy"
+rustflags = ["-C", "default-linker-libraries"]
+
+[env]
+ESP_IDF_SDKCONFIG_DEFAULTS = "sdkconfig.defaults"
+ESP_IDF_VERSION = { value = "branch:release/v4.4" }
+
+ESP_IDF_GLOB_CONFIG_FILES_BASE = { value = ".", relative = true }
+ESP_IDF_GLOB_CONFIG_FILES_1 = { value = "/partitions.csv" }
+
+[unstable]
+build-std = ["std", "panic_abort"]
+
+[net]
+git-fetch-with-cli = true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d7b781
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+target/
+Cargo.lock
+.embuild/
+**/.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/bluedroid.iml b/.idea/bluedroid.iml
new file mode 100644
index 0000000..7025ac1
--- /dev/null
+++ b/.idea/bluedroid.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..59d9a82
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..63c6e02
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..896713f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.16.0)
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(bluedroid)
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..85250ec
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "bluedroid"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+esp-idf-sys = { version = "0.31.6", features = ["native"] }
+log = { version = "0.4.17" }
+lazy_static = { version = "1.4.0" }
+
+[build-dependencies]
+embuild = { version = "0.30.1" }
+anyhow = { version = "1.0.58" }
+
+[dev-dependencies]
+anyhow = { version = "1.0.58" }
+esp-idf-sys = { version = "0.31.4", features = ["native", "binstart"] }
+esp-idf-svc = { version = "0.42.1" }
+
+[[example]]
+name = "server"
+required-features = ["esp-idf-sys/binstart"]
\ No newline at end of file
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..cde35ca
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,4 @@
+fn main() -> anyhow::Result<()> {
+ embuild::build::CfgArgs::output_propagated("ESP_IDF")?;
+ embuild::build::LinkArgs::output_propagated("ESP_IDF")
+}
diff --git a/examples/server.rs b/examples/server.rs
new file mode 100644
index 0000000..16a0a0c
--- /dev/null
+++ b/examples/server.rs
@@ -0,0 +1,31 @@
+use bluedroid::{
+ gatt_server::{GattServer, Application, Service},
+ utilities::ble_uuid::BleUuid,
+};
+use esp_idf_svc;
+use log::info;
+use bluedroid::gatt_server::{Characteristic, Descriptor};
+
+fn main() {
+ esp_idf_sys::link_patches();
+ esp_idf_svc::log::EspLogger::initialize_default();
+
+ info!("Logger initialised.");
+
+ let main_application = Application::new("Main Application", 0x01)
+ .add_service(
+ Service::new("Service 1", BleUuid::from_uuid16(0x0001), true)
+ .add_characteristic(
+ Characteristic::new("Characteristic 1", BleUuid::from_uuid16(0x0001))
+ .add_descriptor(
+ &mut Descriptor::new("Descriptor 1", BleUuid::from_uuid16(0x0001))
+ )
+ )
+ );
+
+ let applications = [main_application];
+
+ let mut s = GattServer::take().unwrap();
+ s.add_applications(&applications);
+ s.start();
+}
diff --git a/partitions.csv b/partitions.csv
new file mode 100644
index 0000000..924878b
--- /dev/null
+++ b/partitions.csv
@@ -0,0 +1,5 @@
+# Name, Type, SubType, Offset, Size, Flags
+# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
+nvs, data, nvs, , 0x6000,
+phy_init, data, phy, , 0x1000,
+factory, app, factory, , 3M,
\ No newline at end of file
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..5fa7a00
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,3 @@
+[toolchain]
+
+channel = "nightly-2022-07-23"
\ No newline at end of file
diff --git a/sdkconfig.defaults b/sdkconfig.defaults
new file mode 100644
index 0000000..cc8fd36
--- /dev/null
+++ b/sdkconfig.defaults
@@ -0,0 +1,14 @@
+CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
+CONFIG_COMPILER_OPTIMIZATION_SIZE=y
+CONFIG_BT_ENABLED=y
+# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
+CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
+CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
+# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set
+# CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE is not set
+CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM=y
+# CONFIG_VFS_SUPPORT_IO is not set
+# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
+# CONFIG_PARTITION_TABLE_TWO_OTA is not set
+CONFIG_PARTITION_TABLE_CUSTOM=y
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..483bc0c
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,6 @@
+# This file was automatically generated for projects
+# without default 'CMakeLists.txt' file.
+
+FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
+
+idf_component_register(SRCS ${app_sources})
diff --git a/src/gatt_server/application.rs b/src/gatt_server/application.rs
new file mode 100644
index 0000000..49b4686
--- /dev/null
+++ b/src/gatt_server/application.rs
@@ -0,0 +1,59 @@
+use crate::gatt_server::service::Service;
+use esp_idf_sys::*;
+use log::info;
+
+#[derive(Debug, Clone)]
+pub struct Application {
+ name: Option,
+ services: Vec,
+ identifier: u16,
+ pub(crate) interface: Option,
+ handle_counter: u16,
+}
+
+impl Application {
+ pub fn new(name: &str, identifier: u16) -> Self {
+ Application {
+ name: Some(String::from(name)),
+ services: Vec::new(),
+ identifier,
+ interface: None,
+ handle_counter: 0,
+ }
+ }
+
+ pub fn add_service(mut self, service: &Service) -> Self {
+ self.services.push(service.clone());
+ self
+ }
+
+ pub(crate) fn generate_handle(&mut self) -> u16 {
+ self.handle_counter += 1;
+ self.handle_counter
+ }
+
+ pub(crate) fn register_self(&self) {
+ info!("Registering {}.", self);
+ unsafe { esp_nofail!(esp_ble_gatts_app_register(self.identifier)) };
+ }
+
+ fn register_services(mut self) {
+ info!("Registering {}'s services.", &self);
+ let handle = self.generate_handle();
+ self.services.iter_mut().for_each(|service| {
+ service.register_self(self.interface.unwrap(), handle);
+ });
+ }
+}
+
+impl std::fmt::Display for Application {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let interface_string = if let Some(interface) = self.interface {
+ format!("{}", interface)
+ } else {
+ String::from("None")
+ };
+
+ write!(f, "{} (0x{:02x}, interface: {})", self.name.clone().unwrap_or_else(|| "Unnamed application".to_string()), self.identifier, interface_string)
+ }
+}
diff --git a/src/gatt_server/characteristic.rs b/src/gatt_server/characteristic.rs
new file mode 100644
index 0000000..ac15ff6
--- /dev/null
+++ b/src/gatt_server/characteristic.rs
@@ -0,0 +1,37 @@
+use std::fmt::Formatter;
+use crate::gatt_server::descriptor::Descriptor;
+use crate::utilities::ble_uuid::BleUuid;
+
+#[derive(Debug, Clone)]
+pub struct Characteristic {
+ name: Option,
+ uuid: BleUuid,
+ value: Vec,
+ descriptors: Vec,
+}
+
+impl Characteristic {
+ pub fn new(name: &str, uuid: BleUuid) -> Characteristic {
+ Characteristic {
+ name: Some(String::from(name)),
+ uuid,
+ value: Vec::new(),
+ descriptors: Vec::new(),
+ }
+ }
+
+ pub fn add_descriptor(&mut self, descriptor: &mut Descriptor) -> &mut Self {
+ self.descriptors.push(descriptor.clone());
+ self
+ }
+
+ fn register_self(&mut self) {}
+
+ fn register_descriptors(&self) {}
+}
+
+impl std::fmt::Display for Characteristic {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{} ({})", self.name.clone().unwrap_or_else(|| "Unnamed characteristic".to_string()), self.uuid)
+ }
+}
diff --git a/src/gatt_server/descriptor.rs b/src/gatt_server/descriptor.rs
new file mode 100644
index 0000000..0077388
--- /dev/null
+++ b/src/gatt_server/descriptor.rs
@@ -0,0 +1,24 @@
+use crate::utilities::ble_uuid::BleUuid;
+
+#[derive(Debug, Clone)]
+pub struct Descriptor {
+ name: Option,
+ uuid: BleUuid,
+ value: Vec,
+}
+
+impl Descriptor {
+ pub fn new(name: &str, uuid: BleUuid) -> Descriptor {
+ Descriptor {
+ name: Some(String::from(name)),
+ uuid,
+ value: Vec::new(),
+ }
+ }
+}
+
+impl std::fmt::Display for Descriptor {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{} ({})", self.name.clone().unwrap_or_else(|| "Unnamed descriptor".to_string()), self.uuid)
+ }
+}
\ No newline at end of file
diff --git a/src/gatt_server/gap_event_handler.rs b/src/gatt_server/gap_event_handler.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/gatt_server/gap_event_handler.rs
@@ -0,0 +1 @@
+
diff --git a/src/gatt_server/gatts_event_handler.rs b/src/gatt_server/gatts_event_handler.rs
new file mode 100644
index 0000000..051b3c1
--- /dev/null
+++ b/src/gatt_server/gatts_event_handler.rs
@@ -0,0 +1,16 @@
+use crate::gatt_server::GattServer;
+use esp_idf_sys::*;
+
+impl GattServer {
+ fn gatts_event_handler(
+ &mut self,
+ event: esp_gatts_cb_event_t,
+ gatts_if: esp_gatt_if_t,
+ param: *mut esp_ble_gatts_cb_param_t,
+ ) {
+ let params = unsafe { (*param).reg };
+ if event == esp_gatts_cb_event_t_ESP_GATTS_REG_EVT && params.status == esp_gatt_status_t_ESP_GATT_OK {
+ // self.applications
+ }
+ }
+}
diff --git a/src/gatt_server/mod.rs b/src/gatt_server/mod.rs
new file mode 100644
index 0000000..2ac9352
--- /dev/null
+++ b/src/gatt_server/mod.rs
@@ -0,0 +1,130 @@
+use std::ptr::replace;
+use std::sync::Mutex;
+
+use esp_idf_sys::*;
+use lazy_static::lazy_static;
+use log::{info, warn};
+
+pub use application::Application;
+pub use characteristic::Characteristic;
+pub use descriptor::Descriptor;
+pub use service::Service;
+
+use crate::leaky_box_raw;
+
+// Structs.
+mod application;
+mod characteristic;
+mod descriptor;
+mod service;
+
+// Event handler.
+mod gap_event_handler;
+mod gatts_event_handler;
+
+lazy_static! {
+ static ref GLOBAL_GATT_SERVER: Mutex