Devicetree implementation WIP

This commit is contained in:
Ken Van Hoeylandt 2026-01-17 14:40:03 +01:00
parent c98cb2bf10
commit e1a72ba9f0
56 changed files with 1367 additions and 24 deletions

View File

@ -0,0 +1,4 @@
.venv/
.idea/
__pycache__/
build/

View File

@ -0,0 +1,27 @@
import os
from source.printing import *
from source.main import *
def print_help():
print("Usage: python compile.py [in_file] [out_path] [arguments]\n")
print(f"\t[in_file] the .dts file")
print(f"\t[out_path] output folder for C file output")
print("")
print("Optional arguments:\n")
print("\t--help prints this help text")
print("\t--verbose output debug info")
if __name__ == "__main__":
if "--help" in sys.argv:
print_help()
sys.exit()
if len(sys.argv) < 3:
print_error("Missing argument")
print_help()
sys.exit()
is_verbose = "--verbose" in sys.argv
devictree_yaml_config = sys.argv[1]
output_path = sys.argv[2]
main(devictree_yaml_config, output_path, is_verbose)

View File

@ -0,0 +1,17 @@
import os
def find_bindings(directory_path: str) -> list[str]:
yaml_files = []
for root, dirs, files in os.walk(directory_path):
for file in files:
if file.endswith(".yaml"):
full_path = os.path.join(root, file)
yaml_files.append(os.path.abspath(full_path))
return yaml_files
def find_all_bindings(directory_paths: list[str]) -> list[str]:
yaml_files = []
for directory_path in directory_paths:
new_paths = find_bindings(directory_path)
yaml_files += new_paths
return yaml_files

View File

@ -0,0 +1,54 @@
import yaml
import os
from .models import Binding, BindingProperty
def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
with open(file_path, 'r') as f:
data = yaml.safe_load(f)
description = data.get('description', '')
bus = data.get('bus', None)
properties_dict = {}
# Handle inclusions
includes = data.get('include', [])
for include_file in includes:
include_path = None
for binding_dir in binding_dirs:
potential_path = os.path.join(binding_dir, include_file)
if os.path.exists(potential_path):
include_path = potential_path
break
if not include_path:
print(f"Warning: Could not find include file {include_file}")
parent_binding = parse_binding(include_path, binding_dirs)
if not description and parent_binding.description:
description = parent_binding.description
if not bus and parent_binding.bus:
bus = parent_binding.bus
for prop in parent_binding.properties:
properties_dict[prop.name] = prop
# Parse local properties
properties_raw = data.get('properties', {})
for name, details in properties_raw.items():
prop = BindingProperty(
name=name,
type=details.get('type', 'unknown'),
required=details.get('required', False),
description=details.get('description', '').strip(),
includes=includes
)
properties_dict[name] = prop
filename = os.path.basename(file_path)
compatible = filename.removesuffix(".yaml").removesuffix(".yml")
return Binding(
filename=filename,
compatible=compatible,
description=description.strip(),
properties=list(properties_dict.values()),
bus=bus
)

View File

@ -0,0 +1,49 @@
from dataclasses import dataclass, field
import yaml
import os
@dataclass
class DeviceTreeConfig:
dependencies: list[str] = field(default_factory=list)
bindings: list[str] = field(default_factory=list)
dts: str = ""
def parse_config(file_path: str, project_root: str) -> list[DeviceTreeConfig]:
"""
Parses devicetree.yaml and recursively finds dependencies.
Returns a list of DeviceTreeConfig objects in post-order (dependencies first).
"""
config = DeviceTreeConfig([], [], "")
visited = set()
def _parse_recursive(current_path: str, is_root: bool):
abs_path = os.path.abspath(current_path)
if abs_path in visited:
return
visited.add(abs_path)
# Try to see if it's a directory and contains devicetree.yaml
if os.path.isdir(abs_path):
abs_path = os.path.join(abs_path, "devicetree.yaml")
with open(abs_path, 'r') as f:
data = yaml.safe_load(f) or {}
# Handle dependencies before adding current config (post-order)
deps = data.get("dependencies", [])
for dep in deps:
# Dependencies are relative to project_root
dep_path = os.path.join(project_root, dep)
_parse_recursive(dep_path, False)
if is_root:
config.dependencies += deps
dts_path = data.get("dts", "")
config.dts = os.path.join(current_path, dts_path)
bindings = data.get("bindings", "")
bindings_resolved = os.path.join(current_path, bindings)
config.bindings.append(bindings_resolved)
_parse_recursive(file_path, True)
return config

View File

@ -0,0 +1,9 @@
def read_file(path: str):
with open(path, "r") as file:
result = file.read()
return result
def write_file(path: str, content: str):
with open(path, "w") as file:
result = file.write(content)
return result

View File

@ -0,0 +1,200 @@
import os.path
from textwrap import dedent
from source.models import *
def write_include(file, include: IncludeC, verbose: bool):
if verbose:
print("Processing include:")
print(f" {include.statement}")
file.write(include.statement)
file.write('\n')
def get_device_identifier_safe(device: Device):
if device.identifier == "/":
return "root"
else:
return device.identifier
def get_device_type_name(device: Device, bindings: list[Binding]):
device_binding = find_device_binding(device, bindings)
if device_binding == None:
raise Exception(f"Binding not found for {device.identifier}")
compatible_safe = device_binding.compatible.split(",")[-1]
return compatible_safe.replace("-", "_")
def find_device_property(device: Device, name: str) -> DeviceProperty:
for property in device.properties:
if property.name == name:
return property
return None
def find_binding_property(binding: Binding, name: str) -> BindingProperty:
for property in binding.properties:
if property.name == name:
return property
return None
def find_device_binding(device: Device, bindings: list[Binding]) -> Binding:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
raise Exception(f"property 'compatible' not found in device {device.identifier}")
for binding in bindings:
if binding.compatible == compatible_property.value:
return binding
return None
def find_binding(compatible: str, bindings: list[Binding]) -> Binding:
for binding in bindings:
if binding.compatible == compatible:
return binding
return None
def property_to_string(property: Property) -> str:
type = property.type
if type == "value":
return property.value
elif type == "text":
return f"\"{property.value}\""
elif type == "values":
return "{ " + ",".join(property.value) + " }"
else:
raise Exception(f"property_to_string() has an unsupported type: {type}")
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding]) -> list:
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
device_binding = find_binding(compatible_property.value, bindings)
if device_binding is None:
raise Exception(f"Binding not found for {device.identifier} and compatible '{compatible_property.value}'")
# Filter out system properties
binding_properties = []
for property in device_binding.properties:
if property.name != "compatible":
binding_properties.append(property)
# Allocate total expected configuration arguments
result = [0] * len(binding_properties)
for index, binding_property in enumerate(binding_properties):
device_property = find_device_property(device, binding_property.name)
if device_property is None:
if binding_property.required:
raise Exception(f"device {device.identifier} doesn't have property '{binding_property.name}'")
else:
result[index] = '0'
else:
result[index] = property_to_string(device_property)
return result
def write_config(file, device: Device, bindings: list[Binding], type_name: str):
device_identifier = get_device_identifier_safe(device)
file.write(f"const static struct {type_name}_config {device_identifier}_config_instance" " = {\n")
config_params = resolve_parameters_from_bindings(device, bindings)
# Indent all params
for index, config_param in enumerate(config_params):
config_params[index] = f"\t{config_param}"
# Join with comman and newline
if len(config_params) > 0:
config_params_joined = ",\n".join(config_params)
file.write(f"{config_params_joined}\n")
file.write("};\n\n")
def write_device(file, device: Device, parent_device: Device, bindings: list[Binding], verbose: bool):
if verbose:
print(f"Processing device '{device.identifier}'")
# Assemble some pre-requisites
type_name = get_device_type_name(device, bindings)
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
identifier = get_device_identifier_safe(device)
# Write config struct
write_config(file, device, bindings, type_name)
# Type & instance names
api_type_name = f"{type_name}_api"
api_instance_name = f"{type_name}_api_instance"
init_function_name = f"{type_name}_init"
deinit_function_name = f"{type_name}_deinit"
config_instance_name = f"{identifier}_config_instance"
# Write device struct
file.write(f"extern const struct {api_type_name} {api_instance_name};\n\n")
file.write("static struct device " f"{identifier}" " = {\n")
file.write("\t.name = \"" f"{identifier}" "\",\n")
file.write(f"\t.config = &{config_instance_name},\n")
file.write(f"\t.api = &{api_instance_name},\n")
file.write("\t.state = { .init_result = 0, .initialized = false },\n")
file.write("\t.data = nullptr,\n")
file.write("\t.operations = { ")
file.write(f".init = {init_function_name}, ")
file.write(f".deinit = {deinit_function_name}")
file.write("},\n")
file.write("\t.metadata = {\n")
file.write("\t\t.num_node_labels = 0,\n")
file.write("\t\t.node_labels = nullptr\n")
file.write("\t}\n")
file.write("};\n\n")
for child_device in device.devices:
write_device(file, child_device, device, bindings, verbose)
def write_device_list_entry(file, device: Device):
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
identifier = get_device_identifier_safe(device)
file.write(f"\t&{identifier},\n")
for child in device.devices:
write_device_list_entry(file, child)
def write_device_list(file, devices: list[Device]):
file.write("struct device* devices_builtin[] = {\n")
for device in devices:
write_device_list_entry(file, device)
# Terminator
file.write(f"\tnullptr\n")
file.write("};\n\n")
def generate_devicetree_c(filename: str, items: list[object], bindings: list[Binding], verbose: bool):
with open(filename, "w") as file:
# Write all headers first
for item in items:
if type(item) is IncludeC:
write_include(file, item, verbose)
file.write("\n")
# Then write all devices
devices = []
for item in items:
if type(item) is Device:
devices.append(item)
write_device(file, item, None, bindings, verbose)
write_device_list(file, devices)
file.write(dedent('''\
struct device** devices_builtin_get() {
return devices_builtin;
}
'''))
def generate_devicetree_h(filename: str):
with open(filename, "w") as file:
file.write(dedent('''\
#pragma once
#include <device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @return an array of device* where the last item in the array is nullptr
*/
struct device** devices_builtin_get();
#ifdef __cplusplus
}
#endif
'''))
def generate(output_path: str, items: list[object], bindings: list[Binding], verbose: bool):
devicetree_c_filename = os.path.join(output_path, "devicetree.c")
generate_devicetree_c(devicetree_c_filename, items, bindings, verbose)
devicetree_h_filename = os.path.join(output_path, "devicetree.h")
generate_devicetree_h(devicetree_h_filename)

View File

@ -0,0 +1,47 @@
%import common.DIGIT -> DIGIT
%import common.LETTER -> LETTER
%import common.HEXDIGIT -> HEXDIGIT
%import common.SIGNED_INT -> SIGNED_INT
%import common.WS -> WS
%import common.SIGNED_NUMBER -> SIGNED_NUMBER
%import common.ESCAPED_STRING -> ESCAPED_STRING
%ignore WS
// Commment
COMMENT: /\/\*[^\/]*\*\//
%ignore COMMENT
// Boolean
BOOLEAN: "true" | "false"
// Main
INCLUDE_C: /#include <[\w\/.\-]+>/
PROPERTY_NAME: /#?[a-zA-Z0-9_\-,]+/
hex_number: /0x[0-9a-fA-F]+/
QUOTE: "\""
QUOTED_TEXT: QUOTE /[^"]+/ QUOTE
quoted_text_array: QUOTED_TEXT ("," " "* QUOTED_TEXT)+
HEX_NUMBER: "0x" HEXDIGIT*
NUMBER: SIGNED_NUMBER | HEX_NUMBER
PHANDLE: /&[0-9a-zA-Z\-]+/
C_VARIABLE: /[0-9a-zA-Z_]+/
VALUE: NUMBER | PHANDLE | C_VARIABLE
value: VALUE
values: VALUE+
array: NUMBER+
property_value: quoted_text_array | QUOTED_TEXT | "<" value ">" | "<" values ">" | "[" array "]"
device_property: PROPERTY_NAME ["=" property_value] ";"
DEVICE_IDENTIFIER: /[a-zA-Z0-9_\-\/@]+/
device: DEVICE_IDENTIFIER "{" (device | device_property)* "};"
dts_version: /[0-9a-zA-Z\-]+/
start: "/" dts_version "/;" INCLUDE_C* device+

View File

@ -0,0 +1,47 @@
import os
from pprint import pprint
from lark import Lark
from source.files import *
from source.transformer import *
from source.generator import *
from source.binding_files import find_all_bindings
from source.binding_parser import parse_binding
from source.config import *
def main(config_path: str, output_path: str, verbose: bool):
print(f"Generating devicetree code\n config: {config_path}\n output: {output_path}")
if not os.path.isdir(config_path):
raise Exception(f"Not found: {config_path}")
config = parse_config(config_path, os.getcwd())
if verbose:
pprint(config)
dts_file_path: str
project_dir = os.path.dirname(os.path.realpath(__file__))
grammar_path = os.path.join(project_dir, "grammar.lark")
lark_data = read_file(grammar_path)
dts_data = read_file(config.dts)
lark = Lark(lark_data)
parsed = lark.parse(dts_data)
if verbose:
print(parsed.pretty())
transformed = DtsTransformer().transform(parsed)
if verbose:
pprint(transformed)
binding_files = find_all_bindings(config.bindings)
if verbose:
print(f"Bindings found:")
for binding_file in binding_files:
print(f" {binding_file}")
if verbose:
print(f"Parsing bindings")
bindings = []
for binding_file in binding_files:
bindings.append(parse_binding(binding_file, config.bindings))
if verbose:
for binding in bindings:
pprint(binding)
generate(output_path, transformed, bindings, verbose)

View File

@ -0,0 +1,42 @@
from dataclasses import dataclass
@dataclass
class DtsVersion:
version: str
@dataclass
class Device:
identifier: str
properties: list
devices: list
@dataclass
class Property:
name: str
type: str
value: object
@dataclass
class PropertyValue:
type: str
value: object
@dataclass
class IncludeC:
statement: str
@dataclass
class BindingProperty:
name: str
type: str
required: bool
description: str
includes: list[str]
@dataclass
class Binding:
filename: str
compatible: str
description: str
properties: list[BindingProperty]
bus: str = None

View File

@ -0,0 +1,20 @@
import sys
if sys.platform == "win32":
SHELL_COLOR_RED = ""
SHELL_COLOR_ORANGE = ""
SHELL_COLOR_RESET = ""
else:
SHELL_COLOR_RED = "\033[91m"
SHELL_COLOR_ORANGE = "\033[93m"
SHELL_COLOR_RESET = "\033[m"
def print_warning(message):
print(f"{SHELL_COLOR_ORANGE}WARNING: {message}{SHELL_COLOR_RESET}")
def print_error(message):
print(f"{SHELL_COLOR_RED}ERROR: {message}{SHELL_COLOR_RESET}")
def exit_with_error(message):
print_error(message)
sys.exit(1)

View File

@ -0,0 +1,64 @@
from typing import List
from lark import Transformer
from lark import Token
from source.models import *
def flatten_token_array(tokens: List[Token], name: str):
result_list = list()
for token in tokens:
result_list.append(token.value)
return Token(name, result_list)
class DtsTransformer(Transformer):
# Flatten the start node into a list
def start(self, tokens):
return tokens
def dts_version(self, tokens: List[Token]):
version = tokens[0].value
if version != "dts-v1":
raise Exception(f"Unsupported DTS version: {version}")
return DtsVersion(version)
def device(self, tokens: list):
identifier = "UNKNOWN"
properties = list()
devices = list()
for index, entry in enumerate(tokens):
if index == 0:
identifier = entry.value
elif type(entry) is Property:
properties.append(entry)
elif type(entry) is Device:
devices.append(entry)
return Device(identifier, properties, devices)
def device_property(self, objects: List[object]):
assert len(objects) == 2
if not type(objects[1]) is PropertyValue:
raise Exception(f"Object was not converted to PropertyValue: {objects[1]}")
return Property(objects[0], objects[1].type, objects[1].value)
def property_value(self, tokens: List):
token = tokens[0]
if type(token) is Token:
raise Exception(f"Failed to convert token to PropertyValue: {token}")
return token
def values(self, object):
return PropertyValue(type="values", value=object)
def value(self, object):
return PropertyValue(type="value", value=object[0])
def array(self, object):
return PropertyValue(type="array", value=object)
def VALUE(self, token: Token):
return token.value
def NUMBER(self, token: Token):
return token.value
def PROPERTY_NAME(self, token: Token):
return token.value
def QUOTED_TEXT(self, token: Token):
return PropertyValue("text", token.value[1:-1])
def quoted_text_array(self, tokens: List[Token]):
result_list = list()
for token in tokens:
result_list.append(token.value)
return PropertyValue("text_array", result_list)
def INCLUDE_C(self, token: Token):
return IncludeC(token.value)

View File

@ -1,7 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_CXX_COMPILER_TARGET}") set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_CXX_COMPILER_TARGET}")
include("Buildscripts/logo.cmake") include("Buildscripts/logo.cmake")
@ -13,15 +12,21 @@ set(Cyan "${Esc}[36m")
file(READ version.txt TACTILITY_VERSION) file(READ version.txt TACTILITY_VERSION)
add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}") add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}")
# Determine device identifier and project location
if (DEFINED ENV{ESP_IDF_VERSION})
include("Buildscripts/device.cmake")
init_tactility_globals("sdkconfig")
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
get_property(TACTILITY_DEVICE_ID GLOBAL PROPERTY TACTILITY_DEVICE_ID)
else ()
set(TACTILITY_DEVICE_PROJECT "Devices/simulator")
set(TACTILITY_DEVICE_ID "simulator")
endif ()
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})
message("Using ESP-IDF ${Cyan}v$ENV{ESP_IDF_VERSION}${ColorReset}") message("Using ESP-IDF ${Cyan}v$ENV{ESP_IDF_VERSION}${ColorReset}")
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
include("Buildscripts/device.cmake")
init_tactility_globals("sdkconfig")
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
set(COMPONENTS Firmware) set(COMPONENTS Firmware)
set(EXTRA_COMPONENT_DIRS set(EXTRA_COMPONENT_DIRS
"Firmware" "Firmware"
@ -75,6 +80,8 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
add_subdirectory(Libraries/QRCode) add_subdirectory(Libraries/QRCode)
add_subdirectory(Libraries/minitar) add_subdirectory(Libraries/minitar)
add_subdirectory(Libraries/minmea) add_subdirectory(Libraries/minmea)
add_subdirectory(Drivers/drivers-abstract)
add_subdirectory(Drivers/drivers-core)
# FreeRTOS # FreeRTOS
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "") set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "")

View File

@ -1,7 +1,12 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*) file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_C_STANDARD 23)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source" INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 PwmBacklight driver esp_adc REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 PwmBacklight driver esp_adc drivers-esp
) )
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)

View File

@ -0,0 +1,18 @@
#include "tlora_pager.h"
#include <esp_log.h>
#include <stdio.h>
#define TAG "tlora_pager"
const struct tlora_pager_api tlora_pager_api_instance = {
};
int tlora_pager_init(const struct device* device) {
ESP_LOGI(TAG, "init %s", device->name);
return 0;
}
int tlora_pager_deinit(const struct device* device) {
ESP_LOGI(TAG, "deinit %s", device->name);
return 0;
}

View File

@ -0,0 +1,22 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <device.h>
#include <drivers/root.h>
// Inherit base config
#define tlora_pager_config root_config
// Inherit base API
#define tlora_pager_api root_api
int tlora_pager_init(const struct device* device);
int tlora_pager_deinit(const struct device* device);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,4 @@
dependencies:
- Drivers/drivers-esp
bindings: ./
dts: lilygo,tlora_pager.dts

View File

@ -0,0 +1,3 @@
description: LilyGO T-Lora Pager
include: ["root.yaml"]

View File

@ -0,0 +1,22 @@
/dts-v1/;
#include <drivers/tlora_pager.h>
#include <drivers-esp/esp32_gpio.h>
#include <drivers-esp/esp32_i2c.h>
/ {
model = "LilyGO T-Lora Pager";
compatible = "lilygo,tlora-pager";
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
i2c0 {
compatible = "espressif,esp32-i2c";
clock-frequency = <100000>;
pin-sda = <&gpio0 3 GPIO_ACTIVE_HIGH>;
pin-scl = <&gpio0 2 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -0,0 +1,4 @@
dependencies:
- Drivers/drivers-abstract
bindings: ./
dts: simulator.dts

View File

@ -0,0 +1,9 @@
/dts-v1/;
#include <device.h>
#include <drivers/root.h>
/ {
model = "Simulator";
compatible = "root";
};

View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.20)
file(GLOB_RECURSE SOURCES "source/*.c**")
if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "include/"
REQUIRES drivers-core
)
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
else ()
add_library(drivers-abstract OBJECT)
target_sources(drivers-abstract PRIVATE ${SOURCES})
target_include_directories(drivers-abstract PUBLIC include/)
target_link_libraries(drivers-abstract PUBLIC drivers-core)
target_compile_options(drivers-abstract PRIVATE -std=c23)
endif ()

View File

@ -0,0 +1,6 @@
properties:
gpio-count:
type: int
required: true
description: |
The number of available GPIOs.

View File

@ -0,0 +1,10 @@
bus: i2c
properties:
clock-frequency:
type: int
description: Initial clock frequency in Hz
pin-sda:
type: phandle-array
pin-scl:
type: phandle-array

View File

@ -0,0 +1,6 @@
on-bus: i2c
properties:
register:
required: true
description: device address on the bus

View File

@ -0,0 +1,4 @@
properties:
model:
required: true
description: the name of hardware, usually vendor and model name

View File

@ -0,0 +1 @@
bindings: bindings

View File

@ -0,0 +1,84 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <device.h>
#define GPIO_OPTIONS_MASK 0x1f
#define GPIO_ACTIVE_HIGH (0 << 0)
#define GPIO_ACTIVE_LOW (1 << 0)
#define GPIO_UNIDIRECTIONAL (0 << 1)
#define GPIO_BIDIRECTIONAL (1 << 1)
#define GPIO_OPEN_DRAIN (GPIO_UNIDIRECTIONAL | (0 << 2))
#define GPIO_OPEN_SOURCE (GPIO_UNIDIRECTIONAL | (1 << 2))
#define GPIO_PULL_UP (0 << 3)
#define GPIO_PULL_DOWN (1 << 4)
#define GPIO_INTERRUPT_WAKE_UP (1 << 5)
/**
* @brief Provides a type to hold a GPIO pin index.
*
* This reduced-size type is sufficient to record a pin number,
* e.g. from a devicetree GPIOS property.
*/
typedef uint8_t gpio_pin_t;
/**
* @brief Identifies a set of pins associated with a port.
*
* The pin with index n is present in the set if and only if the bit
* identified by (1U << n) is set.
*/
typedef uint32_t gpio_pinset_t;
/**
* @brief Provides a type to hold GPIO devicetree flags.
*
* All GPIO flags that can be expressed in devicetree fit in the low 16
* bits of the full flags field, so use a reduced-size type to record
* that part of a GPIOS property.
*
* The lower 8 bits are used for standard flags. The upper 8 bits are reserved
* for SoC specific flags.
*/
typedef uint16_t gpio_flags_t;
/**
* @brief Container for GPIO pin information specified in dts files
*
* This type contains a pointer to a GPIO device, pin identifier for a pin
* controlled by that device, and the subset of pin configuration
* flags which may be given in devicetree.
*/
struct gpio_pin_config {
/** GPIO device controlling the pin */
const struct device* port;
/** The pin's number on the device */
gpio_pin_t pin;
/** The pin's configuration flags as specified in devicetree */
gpio_flags_t dt_flags;
};
/**
* @brief Validate that GPIO port is ready.
*
* @param spec GPIO specification from devicetree
*
* @retval true if the GPIO spec is ready for use.
* @retval false if the GPIO spec is not ready for use.
*/
inline bool gpio_is_ready(const struct gpio_pin_config* pin_config) {
return device_is_ready(pin_config->port);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,35 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "gpio.h"
struct gpio_controller_config {
uint8_t gpio_count;
};
struct gpio_controller_api {
bool (*set_level)(const struct device*, gpio_pin_t pin, bool high);
bool (*get_level)(const struct device*, gpio_pin_t pin, bool* high);
bool (*set_options)(const struct device*, gpio_pin_t pin, gpio_flags_t options);
bool (*get_options)(const struct device*, gpio_pin_t pin, gpio_flags_t* options);
};
#define GPIO_API(dev) ((struct gpio_controller_api*)dev->api)
#define GPIO_CONFIG(dev) ((struct gpio_controller_config*)dev->config)
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high);
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high);
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options);
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options);
inline bool gpio_set_options_config(const struct gpio_pin_config* config) {
return gpio_controller_set_options(config->port, config->pin, config->dt_flags);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,23 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "gpio.h"
struct i2c_controller_config {
uint32_t clock_frequency;
struct gpio_pin_config pin_sda;
struct gpio_pin_config pin_scl;
};
struct i2c_controller_api {
bool (*master_read)(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize); // TODO: add timeout
bool (*master_write)(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize); // TODO: add timeout
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
struct root_config {
const char* model;
};
struct root_api {
};
#define root_init nullptr
#define root_deinit nullptr
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,17 @@
#include <drivers/gpio_controller.h>
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high) {
return GPIO_API(dev)->set_level(dev, pin, high);
}
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
return GPIO_API(dev)->get_level(dev, pin, high);
}
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
return GPIO_API(dev)->set_options(dev, pin, options);
}
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
return GPIO_API(dev)->get_options(dev, pin, options);
}

View File

@ -0,0 +1,5 @@
#include "drivers/i2c_controller.h"
#if !defined(CONFIG_I2C_CONTROLLER_DEVICE_COUNT_LIMIT)
#define CONFIG_I2C_CONTROLLER_DEVICE_COUNT_LIMIT 4
#endif

View File

@ -0,0 +1,3 @@
#include <drivers/root.h>
const struct root_api root_api_instance = {};

View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.20)
file(GLOB_RECURSE SOURCES "source/*.c**")
if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "include/"
)
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
else ()
add_library(drivers-core OBJECT)
target_sources(drivers-core PRIVATE ${SOURCES})
target_include_directories(drivers-core PUBLIC include/)
target_compile_options(drivers-core PRIVATE -std=c23)
endif ()

View File

@ -0,0 +1,88 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
struct device;
struct device_operations {
/** Initialization function */
int (*init)(const struct device* device);
/** De-initialization function */
int (*deinit)(const struct device* device);
};
struct device_state {
uint8_t init_result;
bool initialized : 1;
};
#define DEVICE_LABEL(key, value) #key":"#value
#define DEVICE_LABEL_FOR_TYPE(_type) DEVICE_LABEL(type, _type)
struct device_metadata {
/* @brief number of elements in the nodelabels array */
size_t num_node_labels;
/* @brief array of node labels as strings, exactly as they
* appear in the final devicetree
*/
const char** node_labels;
};
struct device {
/** Name of the device as defined in the dts file */
const char* name;
/** Address of device instance configuration. This relates to the parameters that are specified in the dts file*/
const void* config;
/** Address of the API exposed by the device instance */
const void* api;
/** The device state */
struct device_state state;
/** Address of the device's private data */
void* data;
/** Device operations: used for initializing and deinitializing */
struct device_operations operations;
/** Device metadata */
struct device_metadata metadata;
};
uint8_t device_init(struct device* dev);
bool device_init_all(struct device** device_array);
uint8_t device_deinit(struct device* dev);
/**
* Indicated whether the device is in a state where its API is available
*
* @param[in] dev non-null device pointer
* @return true if the device is ready for use
*/
bool device_is_ready(const struct device* dev);
/**
* Register a single device.
*
* @param[in] dev non-null device pointer
*/
bool device_add(const struct device* dev);
/**
* Register all devices in the specified array.
* The array must be null-terminated.
*
* @param[in] device_array non-null array of devices
*/
void device_add_all(struct device** device_array);
bool device_remove(const struct device* dev);
bool device_find_next(const char* label, const struct device** device);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,119 @@
#include "device.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define CONFIG_DEVICE_INDEX_SIZE 64
// TODO: Automatically increase allocated size
static const struct device* device_index[128] = { 0 };
uint8_t device_init(struct device* const dev) {
assert(dev != nullptr);
printf("device_init: %s\n", dev->name);
if (!dev->state.initialized) {
if (dev->operations.init == nullptr) {
dev->state.initialized = true;
dev->state.init_result = 0U;
} else {
dev->state.init_result = dev->operations.init(dev);
dev->state.initialized = dev->state.init_result == 0U;
}
}
return dev->state.init_result;
}
bool device_init_all(struct device** const device_array) {
assert(device_array != nullptr);
struct device** current_device = device_array;
bool all_succeeded = true;
while (*current_device != nullptr) {
struct device* device = *current_device;
if (device_init(device) != 0U) {
all_succeeded = false;
}
current_device++;
}
return all_succeeded;
}
uint8_t device_deinit(struct device* const dev) {
assert(dev != nullptr);
if (dev->state.initialized) {
if (dev->operations.deinit != nullptr) {
dev->state.init_result = dev->operations.deinit(dev);
if (dev->state.init_result == 0U) {
dev->state.initialized = false;
}
} else {
dev->state.initialized = false;
}
}
return !dev->state.init_result;
}
bool device_is_ready(const struct device* const dev) {
assert(dev != nullptr);
return dev->state.initialized && (dev->state.init_result == 0U);
}
bool device_add(const struct device* dev) {
assert(dev != nullptr);
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
if (device_index[i] == nullptr) {
device_index[i] = dev;
return true;
}
}
return false;
}
void device_add_all(struct device** const device_array) {
assert(device_array != nullptr);
struct device** current_device = device_array;
while (*current_device != nullptr) {
struct device* device = *current_device;
device_add(device);
current_device++;
}
}
bool device_remove(const struct device* dev) {
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
if (device_index[i] == dev) {
device_index[i] = nullptr;
return true;
}
}
return false;
}
bool device_find_next(const char* label, const struct device** device) {
bool found_first = (device == nullptr);
for (int device_idx = 0; device_idx < CONFIG_DEVICE_INDEX_SIZE; device_idx++) {
auto indexed_device = device_index[device_idx];
if (indexed_device != nullptr) {
if (!found_first) {
if (indexed_device == *device) {
found_first = true;
}
} else {
for (int label_idx = 0; label_idx< indexed_device->metadata.num_node_labels; ++label_idx) {
const char* indexed_device_label = indexed_device->metadata.node_labels[label_idx];
if (strcmp(indexed_device_label, label) == 0) {
*device = indexed_device;
return true;
}
}
}
}
}
return false;
}

View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.20)
file(GLOB_RECURSE SOURCES "source/*.c**")
if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "include/"
REQUIRES drivers-abstract driver
)
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
else ()
add_library(drivers-esp OBJECT)
target_sources(drivers-esp PRIVATE ${SOURCES})
target_include_directories(drivers-esp PUBLIC include/)
target_link_libraries(drivers-esp PUBLIC drivers-abstract)
target_compile_options(drivers-esp PRIVATE -std=c23)
endif ()

View File

@ -0,0 +1,3 @@
description: ESP32 GPIO Controller
include: ["gpio-controller.yaml"]

View File

@ -0,0 +1,3 @@
description: ESP32 GPIO Controller
include: ["i2c-controller.yaml"]

View File

@ -0,0 +1,3 @@
dependencies:
- Drivers/drivers-abstract
bindings: bindings

View File

@ -0,0 +1,19 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <device.h>
#include <drivers/gpio_controller.h>
#define esp32_gpio_config gpio_controller_config
#define esp32_gpio_api gpio_controller_api
int esp32_gpio_init(const struct device* device);
int esp32_gpio_deinit(const struct device* device);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,22 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <device.h>
#include <drivers/i2c_controller.h>
// Inherit base config
#define esp32_i2c_config i2c_controller_config
// Inherit base API
#define esp32_i2c_api i2c_controller_api
int esp32_i2c_init(const struct device* device);
int esp32_i2c_deinit(const struct device* device);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,48 @@
#include <drivers-esp/esp32_gpio.h>
#include <drivers/gpio_controller.h>
#include <drivers/gpio.h>
// ESP
#include <esp_log.h>
#include <driver/gpio.h>
#define TAG "esp32_gpio"
static bool set_level(const struct device* dev, gpio_pin_t pin, bool high) {
return gpio_set_level(pin, high) == ESP_OK;
}
static bool get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
*high = gpio_get_level(pin) != 0;
return true;
}
static bool set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
// TODO
// gpio_set_direction()
// gpio_set_pull_mode()
return true;
}
static bool get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
// gpio_get_direction()
// gpio_get_pull_mode()
// TODO
return true;
}
const struct esp32_gpio_api esp32_gpio_api_instance = {
.set_level = set_level,
.get_level = get_level,
.set_options = set_options,
.get_options = get_options
};
int esp32_gpio_init(const struct device* device) {
ESP_LOGI(TAG, "init %s", device->name);
return 0;
}
int esp32_gpio_deinit(const struct device* device) {
ESP_LOGI(TAG, "deinit %s", device->name);
return 0;
}

View File

@ -0,0 +1,31 @@
#include "drivers-esp/esp32_i2c.h"
#include <drivers/i2c_controller.h>
#include <esp_log.h>
#include <stdio.h>
#define TAG "esp32_i2c"
#define GET_CONFIG(dev) static_cast<struct i2c_esp_config>(dev->config)
static bool master_read(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize) {
return true;
}
static bool master_write(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize) {
return true;
}
const struct esp32_i2c_api esp32_i2c_api_instance = {
.master_read = master_read,
.master_write = master_write
};
int esp32_i2c_init(const struct device* device) {
ESP_LOGI(TAG, "init %s", device->name);
return 0;
}
int esp32_i2c_deinit(const struct device* device) {
ESP_LOGI(TAG, "deinit %s", device->name);
return 0;
}

1
Firmware/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
Generated/

View File

@ -1,29 +1,66 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_C_STANDARD 23)
file(GLOB_RECURSE SOURCE_FILES "Source/*.c*") file(GLOB_RECURSE SOURCE_FILES "Source/*.c*")
# Determine device identifier and project location
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})
# Read device id/project
include("../Buildscripts/device.cmake") include("../Buildscripts/device.cmake")
init_tactility_globals("../sdkconfig") init_tactility_globals("../sdkconfig")
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT) get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
else ()
set(TACTILITY_DEVICE_PROJECT "Devices/simulator")
endif ()
set(DEVICETREE_LOCATION "Devices/${TACTILITY_DEVICE_ID}")
if (NOT DEFINED ${COMPONENT_LIB})
set(COMPONENT_LIB FirmwareSim)
endif ()
if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
REQUIRES ${DEVICE_COMPONENTS} REQUIRES ${DEVICE_COMPONENTS}
REQUIRES Tactility TactilityC ${TACTILITY_DEVICE_PROJECT} REQUIRES Tactility TactilityC drivers-core drivers-abstract drivers-esp ${TACTILITY_DEVICE_PROJECT}
) )
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
else () else ()
add_executable(FirmwareSim ${SOURCE_FILES}) add_executable(FirmwareSim ${SOURCE_FILES} "${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c")
target_link_libraries(FirmwareSim target_link_libraries(FirmwareSim
PRIVATE Tactility PRIVATE Tactility
PRIVATE TactilityCore PRIVATE TactilityCore
PRIVATE TactilityFreeRtos PRIVATE TactilityFreeRtos
PRIVATE Simulator PRIVATE Simulator
PRIVATE SDL2::SDL2-static SDL2-static PRIVATE SDL2::SDL2-static SDL2-static
PRIVATE drivers-abstract
PRIVATE drivers-core
) )
target_include_directories(FirmwareSim PRIVATE "${CMAKE_SOURCE_DIR}/Firmware/Generated")
target_compile_options(FirmwareSim PRIVATE -std=c++23)
add_definitions(-D_Nullable=) add_definitions(-D_Nullable=)
add_definitions(-D_Nonnull=) add_definitions(-D_Nonnull=)
add_dependencies(FirmwareSim Generated)
endif () endif ()
# Generate devicetree code and attach to component
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.h
COMMAND mkdir ${CMAKE_SOURCE_DIR}/Firmware/Generated | pip install lark pyyaml && python Buildscripts/devicetree-compiler/compile.py ${DEVICETREE_LOCATION} Firmware/Generated
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
VERBATIM
)
add_custom_target(Generated DEPENDS ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.h)
set_property(DIRECTORY "${CMAKE_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES "Firmware/Generated/devicetree.c" "/Firmware/Generated/devicetree.h")
# Attach generated code to project
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c")
target_include_directories(${COMPONENT_LIB} PRIVATE ${CMAKE_SOURCE_DIR}/Firmware/Generated)
add_dependencies(${COMPONENT_LIB} Generated)

View File

@ -1,4 +1,5 @@
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <devicetree.h>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <tt_init.h> #include <tt_init.h>
@ -24,7 +25,8 @@ void app_main() {
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32 tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
#endif #endif
tt::run(config); auto devices = devices_builtin_get();
tt::run(config, devices);
} }
} // extern } // extern

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})
@ -27,7 +27,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
vfs vfs
fatfs fatfs
lwip lwip
drivers-abstract
drivers-core
) )
if ("${IDF_TARGET}" STREQUAL "esp32s3") if ("${IDF_TARGET}" STREQUAL "esp32s3")
list(APPEND REQUIRES_LIST esp_tinyusb) list(APPEND REQUIRES_LIST esp_tinyusb)
endif () endif ()
@ -49,7 +52,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
# Read-write # Read-write
fatfs_create_spiflash_image(data "${CMAKE_CURRENT_SOURCE_DIR}/../Data/data" FLASH_IN_PROJECT PRESERVE_TIME) fatfs_create_spiflash_image(data "${CMAKE_CURRENT_SOURCE_DIR}/../Data/data" FLASH_IN_PROJECT PRESERVE_TIME)
endif () endif ()
else() else()
file(GLOB_RECURSE SOURCES "Source/*.c*") file(GLOB_RECURSE SOURCES "Source/*.c*")
@ -79,6 +81,8 @@ else()
PUBLIC lv_screenshot PUBLIC lv_screenshot
PUBLIC minmea PUBLIC minmea
PUBLIC minitar PUBLIC minitar
PUBLIC drivers-abstract
PUBLIC drivers-core
) )
endif() endif()

View File

@ -4,6 +4,7 @@
#include <Tactility/app/AppManifest.h> #include <Tactility/app/AppManifest.h>
#include <Tactility/hal/Configuration.h> #include <Tactility/hal/Configuration.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
#include <device.h>
namespace tt { namespace tt {
@ -21,7 +22,7 @@ struct Configuration {
* Attempts to initialize Tactility and all configured hardware. * Attempts to initialize Tactility and all configured hardware.
* @param[in] config * @param[in] config
*/ */
void run(const Configuration& config); void run(const Configuration& config, device** devices);
/** /**
* While technically nullable, this instance is always set if tt_init() succeeds. * While technically nullable, this instance is always set if tt_init() succeeds.

View File

@ -306,7 +306,7 @@ void registerApps() {
registerInstalledAppsFromSdCards(); registerInstalledAppsFromSdCards();
} }
void run(const Configuration& config) { void run(const Configuration& config, device** devices) {
LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID); LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
assert(config.hardware); assert(config.hardware);
@ -315,6 +315,16 @@ void run(const Configuration& config) {
// Assign early so starting services can use it // Assign early so starting services can use it
config_instance = &config; config_instance = &config;
if (devices != nullptr) {
device_add_all(devices);
device_init_all(devices);
const struct device* i2c_controller = nullptr;
const char* label = DEVICE_LABEL_FOR_TYPE(i2c_controller);
while (device_find_next(label, &i2c_controller)) {
}
}
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
initEsp(); initEsp();
#endif #endif

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})
@ -24,9 +24,7 @@ else()
PRIVATE ${SOURCES} PRIVATE ${SOURCES}
) )
target_include_directories(TactilityCore SYSTEM target_include_directories(TactilityCore PUBLIC Include/)
PUBLIC Include/
)
add_definitions(-D_Nullable=) add_definitions(-D_Nullable=)
add_definitions(-D_Nonnull=) add_definitions(-D_Nonnull=)

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})