Skip to content

Add I2C bus selection (TwoWire*) constructor; fix si5351_vcxo example - #107

Merged
NT7S merged 5 commits into
masterfrom
twowire-bus-select
Oct 17, 2026
Merged

NT7S merged 5 commits into
masterfrom
twowire-bus-select

Conversation

@NT7S

@NT7S NT7S commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Version: 2.4.0 (head 8472d61). library.properties, library.json and the README changelog are set to 2.4.0.

Summary

Adds I2C bus selection so the Si5351 can sit on Wire1 or any other TwoWire bus (ESP32, RP2040, SAMD, STM32, Teensy and similar boards).

  • New constructor Si5351(TwoWire *wire = &Wire, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR).
  • Integer address overloads (uint8_t, int, unsigned, long, unsigned long, long long, unsigned long long) keep Si5351(), Si5351(0x61), Si5351(0) and Si5351(NULL) compiling on the address path, as before.
  • A null TwoWire * (nullptr or (TwoWire *)0) falls back to Wire.
  • init() (begin() plus the device probe) and every register read and write go through _wire. Both constructors share one setup_obj() helper, so there is no delegating constructor.
  • README: new "Selecting an I2C Bus" section and a v2.4.0 changelog entry.
  • Separate fix: examples/si5351_vcxo now calls get_correction(SI5351_PLL_INPUT_XO). The old call failed to compile on every core.

Usage

#include "Wire.h"
#include "si5351.h"

Si5351 si5351(&Wire1);           // or Si5351 si5351(&Wire1, 0x61);

void setup()
{
#if defined(ARDUINO_ARCH_ESP32)
  Wire1.setPins(13, 15);         // ESP32: pick SDA/SCL before init()
#endif
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  // Wire1.setClock(400000);     // set the bus speed after init()
}

Compatibility

  • Default bus unchanged: the I2C transcript on the default bus is byte-identical to master (1ddd43e) across 693 scenarios x 2 register-file patterns.
  • Si5351(97.0) (a floating-point address) is now a compile error because the overloads make it ambiguous. It used to convert silently.
  • The constructors are not explicit, the same as before.
  • begin(): init() still calls begin() with no arguments.
    • On ESP32 this keeps pins set with setPins(). If the bus is already running, it is left alone; the core logs "Bus already started in Master Mode", which is harmless and also happens on master.
    • On other cores begin() runs again and may reset an earlier setClock(), so call setClock() after init().

Cost

Uno, basic si5351_example: +138 B flash, +2 B RAM.

Verification

  • Compile matrix: all examples plus a constructor-matrix sketch compile on the 9 boards Test Lab compiled for. This includes uno, esp32, mkrzero, rpipico, STM32 F411, nona4809, nano33ble and esp8266, plus ESP32/RP2040 sketches that use the second bus.
  • Tests: issue-106 tests (t106) 23/23 pass, and the must-not-break suite 776/776.
  • Hardware: verification by the maintainer is still pending.

Independent review

  • Review 1 (fresh reviewer, 69ead04): CHANGES REQUIRED. There was 1 blocking finding (Si5351(0)/Si5351(NULL) were ambiguous) and 6 non-blocking ones (null guard, delegating constructor, README notes on begin()/setClock() and ESP32-only pins, changelog wording, extra tests). All were fixed in ee18a6b.
  • Test Lab re-check of ee18a6b: PASS.
  • Test Lab re-check of a4cfc9a (README wording and the vcxo example fix): PASS.
  • Re-check of abf9b2a (version bump to 2.4.0 and changelog only, no code changes): PASS (Test Lab; only README, library.json, library.properties changed; arduino-lint 0 errors; builds clean).
  • 8472d61 (changelog wording only): docs-only change, no re-review needed.

Related

PR #76 (I2C interface abstraction for STM32) overlaps with this one, but this PR does not replace it: #76 abstracts away the Wire library itself.

Closes #103
Closes #106

NT7S added 5 commits October 15, 2026 07:23
Si5351(TwoWire *wire = &Wire, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR)
selects the bus; Si5351(uint8_t) still selects Wire. init(), the device
probe and all reads/writes go through the selected bus. init() still calls
begin() with no arguments so ESP32 setPins()/RP2040 setSDA() choices are kept.
- Integer-address overloads so Si5351(0), Si5351(NULL) and any integer
  type select the address path as before (no ambiguity with TwoWire *)
- Null TwoWire * falls back to Wire
- Shared setup_obj() helper instead of a C++11 delegating constructor
- README: begin() re-runs in init() on non-ESP32 cores and may reset
  setClock(), so call setClock() after init(); mark setPins() ESP32-only;
  changelog uses SI5351_BUS_BASE_ADDR
- nullptr or (TwoWire *)0 selects Wire via the bus constructor; NULL/0 mean address 0
- Note the harmless ESP32 'Bus already started in Master Mode' log
- si5351_vcxo: get_correction() takes the PLL input; use SI5351_PLL_INPUT_XO
@NT7S
NT7S merged commit f94870f into master Oct 17, 2026
2 checks passed
@NT7S
NT7S deleted the twowire-bus-select branch October 17, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lost the ability to connect the Si5351 via the Etherkit library to an I2C channel other than channel 0. TwoWire Support

1 participant