consync
Bidirectional constant synchronisation between spreadsheets and source code.
Keep a single source of truth for hardware constants (resistor values, timing parameters, frequencies) and automatically generate type-safe declarations for C/C++, C#, Python, Rust, Verilog, and VHDL — or sync changes back from code to your spreadsheet.
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ .consync.yaml │
│ (mappings, precision, options) │
└────────────────────────────────┬────────────────────────────────────────┘
│
┌────────────▼────────────┐
│ Config Loader │
│ (auto-detect formats) │
└────────────┬────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌────────▼───────┐ ┌──────▼──────┐ ┌────────▼───────┐
│ Parsers │ │ State │ │ Renderers │
│ │ │ Tracker │ │ │
│ • xlsx │ │ │ │ • c_header │
│ • csv │ │ MD5 hashes │ │ • c_struct_table│
│ • json │ │ per-mapping │ │ • csharp │
│ • toml │ │ in .json │ │ • python │
│ • c_header │ │ │ │ • rust │
│ • c_struct_table │ │ │ │ • verilog │
└────────┬────────┘ └──────┬──────┘ │ • vhdl │
│ │ │ • json │
│ │ │ • csv │
│ │ └────────┬────────┘
└──────────────────┼───────────────────┘
│
┌────────────▼────────────┐
│ Sync Engine │
│ │
│ 1. Parse source & target │
│ 2. Compare hashes │
│ 3. Detect direction │
│ 4. Render to changed │
│ side's format │
│ 5. Update state hashes │
└────────────┬────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌────────▼───────┐ ┌──────▼──────┐ ┌────────▼───────┐
│ CLI (click) │ │ Watcher │ │ Git Hooks │
│ │ │ (watchdog) │ │ (pre-commit) │
│ sync/check/ │ │ debounced │ │ consync check │
│ watch/init/ │ │ auto-sync │ │ exit 1 = block │
│ status │ │ │ │ │
└─────────────────┘ └─────────────┘ └────────────────┘
Data Flow
Source file ──parse──► list[Constant] ──render──► Target file
.xlsx name: str .h
.csv value: int|float .cs
.json unit: str .py
.toml description: str .rs
.h .v / .vhd
Every format is both a parser and/or a renderer. This means:
- Bootstrap from code: Have a
.hfile but no spreadsheet? Setdirection: target_to_sourceand consync will create the CSV/JSON for you. - Multiple targets from one source: One
.xlsxcan generate.h+.py+.vsimultaneously. - Multiple sources: Track as many source↔target pairs as needed in one config.
Why consync?
In hardware/firmware projects, constants live in Excel spreadsheets (for EE review) and in source code (for compilation). Manual copy-paste introduces:
- Precision loss — Excel shows 6 digits, your code needs 17 for IEEE 754 fidelity
- Drift — spreadsheet updated, code forgotten (or vice versa)
- Format friction — same value needs different syntax in C, Verilog, Python, VHDL
consync eliminates all three: one command syncs constants with full precision in both directions.
Quick Start
pip install consync
1. Initialise
cd your-project/
consync init
Creates .consync.yaml:
mappings:
- source: constants.xlsx
target: constants.h
direction: both
precision: 17
header_guard: HW_CONSTANTS_H
# Or for C struct tables → Excel:
# - source: MotorParams.c
# target: MotorParams.xlsx
# direction: both
# format: c_struct_table
2. Sync
consync sync
Reads constants.xlsx, generates constants.h:
#ifndef HW_CONSTANTS_H
#define HW_CONSTANTS_H
const double R_SENSE = 1.9999999999910001; /* Ohm | Current sense resistor */
const double R_PULLUP = 4706; /* Ohm | I2C pull-up resistor */
const double C_FILTER = 4.783294e-07; /* F | Input filter capacitor */
const double FREQ_SWITCH = 299872.93847293; /* Hz | Switching frequency */
#endif /* HW_CONSTANTS_H */
3. Watch (continuous)
consync watch
Auto-syncs on file change. Edit the spreadsheet → code updates. Edit the code → spreadsheet updates.
4. CI gate
consync check
Returns exit code 1 if code and spreadsheet are out of sync. Add to pre-commit hooks:
consync install-hook
Supported Formats
Sources (input)
| Format | Extension | Notes |
|---|---|---|
| Excel | .xlsx |
Auto-detects flat or table layout (multi-variant) |
| CSV | .csv, .tsv |
Auto-detects delimiter |
| JSON | .json |
Flat, array, nested, or structured (_meta + constants) |
| TOML | .toml |
Flat or table-with-metadata |
| C Header | .h |
Parses const, #define, hex/int/float |
| C Struct Table | .c |
Multi-variant #if/#elif struct arrays (Bosch-style) |
Targets (output)
| Format | Extension | Features |
|---|---|---|
| C Header | .h |
const or #define, static, stdint.h types, hex |
| C Struct Table | .c |
In-place update of multi-variant struct arrays (minimal diffs) |
| C# | .cs |
namespace, public static class, XML doc comments |
| Python | .py |
Type annotations (float/int), inline comments |
| Rust | .rs |
pub const, f64/i64, doc comments |
| Verilog | .v |
parameter real, optional module wrapper |
| VHDL | .vhd |
Package with ieee.math_real, typed constants |
| JSON | .json |
Structured with _meta header |
| CSV | .csv |
Round-trip back to spreadsheet |
| Excel | .xlsx |
Multi-sheet table layout (one sheet per variant) |
Bootstrap from Existing Code
Don't have a spreadsheet yet? consync can create one from your existing source files.
C Struct Table → Excel (multi-variant motor parameters)
Have a complex C file with #if/#elif struct arrays and want an Excel for your team to review/edit?
# .consync.yaml
mappings:
- source: RBPSC_HWVariantParameter.c # Existing C file with struct arrays
target: RBPSC_HWVariantParameter.xlsx # Excel will be created on first sync
direction: both # Edit C or Excel — both stay in sync
format: c_struct_table # Parses #if/#elif multi-variant tables
precision: 17
consync sync
# ✅ RBPSC_HWVariantParameter.c ↔ RBPSC_HWVariantParameter.xlsx: 160 constants synced (source → target)
consync parses the C file — handling nested {{...}} initializers, #if/#elif variant blocks, float suffixes (1.0F), hex (0xFF), expressions — and generates a multi-sheet Excel with one sheet per variant (BWA, EMB, DPB, etc.).
After that, edit either side:
- Edit Excel → values sync back into the C file (in-place, minimal diffs)
- Edit C file → Excel updates on next
consync sync
Note: The
format: c_struct_tablefield tells consync to use the struct-table parser for the.cfile instead of the defaultc_headerparser. The.xlsxtarget format is auto-detected.
C Header → CSV/JSON (simple constants)
If you have a simpler C header with const/#define declarations:
# .consync.yaml
mappings:
- source: params.csv # doesn't exist yet — will be created
target: existing_params.h # already exists with const declarations
direction: target_to_source
consync sync
# ✅ params.csv ↔ existing_params.h: 12 constants synced (target → source)
consync parses the existing .h file, extracts all constants (name, value, unit, description from comments), and writes a fresh CSV. From that point forward, you have a spreadsheet to share with your EE team.
Works with any parseable format: .h, .c, .json, .csv, .toml — anything consync has a parser for can bootstrap a new source.
Multiple Mappings (One Source → Many Targets)
Track as many source↔target pairs as you need. Each mapping is independent — different directions, precision, and output options:
# .consync.yaml
mappings:
# EE team's spreadsheet → firmware header
- source: params.xlsx
target: firmware/params.h
direction: source_to_target
precision: 17
output_style: const
typed_ints: true
# Same spreadsheet → Python simulation
- source: params.xlsx
target: sim/params.py
direction: source_to_target
precision: 10
# Same spreadsheet → FPGA constraints
- source: params.xlsx
target: rtl/params.v
direction: source_to_target
module_name: hw_params
# Different spreadsheet → C# test harness
- source: test_vectors.csv
target: tests/TestConstants.cs
direction: source_to_target
namespace: ECU.Tests
class_name: BrakeParams
# Legacy header → bootstrap a TOML config from it
- source: legacy_config.toml
target: legacy/old_module.h
direction: target_to_source
consync sync processes all mappings in one pass. consync check verifies them all.
Embedded / ECU Configuration
consync is built for embedded firmware engineers. The C header renderer supports:
Output Styles
output_style: const # default: const double X = 1.5;
output_style: define # preprocessor: #define X 1.5
const output (default):
#include <stdint.h>
static const uint16_t BRAKE_PRESSURE_MAX = 250;
static const double BRAKE_GAIN = 1.45;
#define output:
#define BRAKE_PRESSURE_MAX 250
#define BRAKE_GAIN 1.45
#define HW_VERSION 0x0A03
Static Const
static_const: true # adds 'static' keyword (internal linkage, header-safe)
Typed Integers (stdint.h)
typed_ints: true # auto-selects smallest stdint type that fits
| Value Range | Generated Type |
|---|---|
| 0–255 | uint8_t |
| 0–65535 | uint16_t |
| 0–4294967295 | uint32_t |
| 0+ (larger) | uint64_t |
| -128–127 | int8_t |
| -32768–32767 | int16_t |
| -2147483648–2147483647 | int32_t |
| (larger signed) | int64_t |
Hex values are preserved: 0xFF03 stays 0xFF03 in output.
Full Embedded Example
mappings:
- source: ecu_params.csv
target: src/ecu_params.h
direction: source_to_target
precision: 17
header_guard: ECU_PARAMS_H
output_style: const
static_const: true
typed_ints: true
Generates:
#ifndef ECU_PARAMS_H
#define ECU_PARAMS_H
#include <stdint.h>
static const uint8_t BRAKE_PRESSURE_MAX = 250; /* bar | Max hydraulic pressure */
static const uint16_t CAN_MSG_ID = 0x1A3; /* – | Brake status CAN ID */
static const double FILTER_CUTOFF = 1200.5; /* Hz | Low-pass filter cutoff */
static const int16_t TEMP_OFFSET = -40; /* °C | Temperature sensor offset */
#endif /* ECU_PARAMS_H */
Precision Guarantee
consync uses 17 significant digits by default — the minimum needed for IEEE 754 double-precision round-trip fidelity:
Excel value: 1.9999999999910001
C output: 1.9999999999910001
Parse back: 1.9999999999910001 ← identical bits
Configure per mapping:
precision: 6 # fewer digits for display-only targets
Bidirectional Sync
consync tracks file hashes in .consync.state.json to detect which side changed:
| Source changed | Target changed | Action |
|---|---|---|
| ✓ | — | Source → Target |
| — | ✓ | Target → Source |
| ✓ | ✓ | Conflict (configurable: source_wins, target_wins, fail) |
| — | — | No-op |
Configuration Reference
# .consync.yaml
mappings:
- source: constants.xlsx # source file path (xlsx/csv/json/toml/h/c)
target: hw_constants.h # target file path (h/cs/py/rs/v/vhd/json/csv/xlsx)
direction: both # source_to_target | target_to_source | both
precision: 17 # significant digits (1-17, default: 17)
# ─── Format override ───
# format: c_struct_table # Override auto-detected format for the matching side.
# # Useful when .c needs c_struct_table instead of c_header.
# # Auto-assigned to whichever side's extension matches.
# source_format: xlsx # Explicit source format (overrides auto-detect)
# target_format: c_header # Explicit target format (overrides auto-detect)
# ─── C Header options ───
header_guard: HW_CONSTANTS_H # #ifndef guard name
output_style: const # const (default) | define
static_const: true # adds 'static' keyword (default: false)
typed_ints: true # use stdint.h types (default: false)
# ─── C Struct Table options ───
# parser_options:
# variant: DPB # Select specific #elif variant (or "all")
# table_var: PSC_HWVarParLUT # Struct array variable name (auto-detected)
# fields: [R_Phase, L_d, L_q, Psi, J, Imax, Tmax] # Field names (auto-detected)
# ─── C# options ───
namespace: MyCompany.Firmware # C# namespace
class_name: EcuConstants # C# class name (default: Constants)
# ─── Verilog/VHDL options ───
module_name: design_params # Verilog module / VHDL package name
Safety & Recovery
consync is designed for safety-critical embedded systems. Every sync is protected by:
Automatic Backups
Before every write, the previous version is saved to .consync/backups/:
.consync/backups/
├── config.h.20260505_083012.bak
├── config.h.20260505_091045.bak
└── params.v.20260505_083012.bak
Retention: keeps last 20 backups per file (auto-trimmed).
Recovery
Restore any file to a previous state:
# List all available snapshots
consync recover --list
# List snapshots for a specific file
consync recover --file config.h --list
# Restore most recent backup
consync recover --file config.h --last
# Restore to a specific timestamp
consync recover --file config.h --at 2026-05-05T08:30:12
Recovery creates a safety backup of the current state before restoring, so you can always undo an undo.
Diff Preview
See exactly what would change before committing:
consync diff
Shows a colorized unified diff for each mapping that would be modified. Pair with --from source or --from target to preview forced-direction syncs.
Validation Hooks
Define value constraints in .consync.yaml to reject out-of-range values before they reach your firmware:
mappings:
- source: calibration.xlsx
target: brake_params.h
direction: source_to_target
validators:
BRAKE_MAX_PRESSURE:
min: 0
max: 300
TIMEOUT_MS:
type: int
min: 100
max: 60000
DEVICE_NAME:
pattern: "^[A-Z]{3}-\\d{4}$"
LOOKUP_TABLE:
min_length: 4
max_length: 256
min: 0
max: 1023
Supported validator rules:
| Rule | Applies to | Description |
|---|---|---|
min / max |
int, float, array elements | Numeric range (inclusive) |
type |
all | Expected type: int, float, string |
pattern |
string | Regex pattern (Python re.match) |
min_length / max_length |
arrays, strings | Length bounds |
not_empty |
all | Reject "" or [] |
If validation fails, sync is blocked and the error is reported:
❌ calibration.xlsx ↔ brake_params.h: Validation failed: BRAKE_MAX_PRESSURE = 999 exceeds maximum 300
Concurrency Lock
An advisory lock (.consync.lock) prevents concurrent writes:
- Detects stale locks from crashed processes (PID check + timeout)
- Auto-reclaims dead locks
- Prevents file corruption from parallel
consync sync+consync watch
Audit Log
Every sync is logged with full values to .consync.audit.jsonl:
# Show recent syncs with values
consync log
# Show last 5 entries as raw JSON
consync log -n 5 --json
Each entry records: timestamp, user, direction, files, all constant names+values — enabling full traceability for ISO 26262 / IEC 61508 compliance workflows.
CLI Reference
| Command | Description |
|---|---|
consync init |
Create .consync.yaml in current directory |
consync sync |
Sync all mappings |
consync sync --dry-run |
Preview changes without writing |
consync sync --from source |
Force source → target regardless of state |
consync sync --from target |
Force target → source (bootstrap) |
consync check |
Verify sync (exit 1 if out of sync) |
consync watch |
Watch files and auto-sync on change |
consync diff |
Show unified diff of what would change |
consync recover --list |
List available snapshots |
consync recover --file X --last |
Restore most recent backup |
consync log |
Show audit log with values |
consync install-hook |
Install git pre-commit hook |
consync status |
Show current sync state |
-v / --verbose |
Show INFO-level details |
--debug |
Show DEBUG-level details |
Target Audiences
| Domain | Source | Targets | Key Features |
|---|---|---|---|
| Embedded/ECU firmware | Excel/CSV from EE team | C headers | typed_ints, static_const, #define |
| FPGA/ASIC design | Excel from systems team | Verilog + VHDL | module_name, parameter real |
| Automotive (AUTOSAR) | CSV with calibration data | C headers | Hex preservation, stdint.h |
| .NET test harnesses | CSV/JSON | C# classes | namespace, class_name, XML docs |
| Control systems | Excel with tuning params | Python + C | Full precision, bidirectional |
| Multi-language libs | JSON master | C + Python + Rust | One source, many targets |
Development
git clone https://github.com/naveenkumarbaskaran/consync.git
cd consync
pip install -e ".[dev]"
pytest # 261 tests
License
MIT — see LICENSE.
Release files for consync 2.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| consync-2.4.0.tar.gz | 693.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| consync-2.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 767.5 kB
Release files / consync-2.4.0.tar.gz
| Download URL | consync-2.4.0.tar.gz |
|---|---|
| Size | 693.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60a1660cc30cade4dcf3e9569d056f99857b89ebc9c9e64dd15df4f25a8d0ed8
|
|
BLAKE2b-256 checksum How to use checksums |
98ba77c88b0d049502ea4b4f9e7d595af9e98a74165f626cf23915626353ab71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / consync-2.4.0-py3-none-any.whl
| Download URL | consync-2.4.0-py3-none-any.whl |
|---|---|
| Size | 73.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d4f5303f22e402ce27b9f908b15370be74750256245866c6778b19f868df5a41
|
|
BLAKE2b-256 checksum How to use checksums |
2b685f7a6e326f969ee38527c275ed272df29aa1984e87a0d5ee109103605874
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|