Skip to content

Muzi Works R1 Neo support#2007

Merged
liamcottle merged 4 commits intomeshcore-dev:devfrom
khudson:r1neo
Apr 1, 2026
Merged

Muzi Works R1 Neo support#2007
liamcottle merged 4 commits intomeshcore-dev:devfrom
khudson:r1neo

Conversation

@khudson
Copy link
Copy Markdown
Contributor

@khudson khudson commented Mar 12, 2026

Resolves #1275.

Adds support for Muzi Works R1 Neo device.

Adds new RTC: Epson Seiko RX8130CE.

All features on the board currently work with the exception of software shutdown. Workaround: Hold the button for 8 seconds to put the unit into DFU mode with the USB cable disconnected. The device will shut down.

khudson added 2 commits March 8, 2026 11:46
Support for R1 Neo hardware. New variant and baseboard class.
* Known issues:
  - power management is not currently supported
  - power off via long button press is not implemented

Add support for Epson Seiko RX8130CE I2C Real-time clock.
Copy link
Copy Markdown
Contributor

@weebl2000 weebl2000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx for the implementation @khudson - I hope my review is useful ;)

buf[1] = bin2bcd(t->tm_sec) & 0x7F;
buf[2] = bin2bcd(t->tm_min) & 0x7F;
buf[3] = bin2bcd(t->tm_hour) & 0x3F;
buf[4] = bin2bcd(t->tm_wday) & 0x07;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RX8130CE WEEK register uses a bitmap format (bit 0 = Sun, bit 1 = Mon, ..., bit 6 = Sat), not BCD. For example Wednesday (tm_wday=3) should be 0x08 (bit 3), but bin2bcd(3) gives 0x03 (bits 0+1 = Sun+Mon). The ArtronShop library has the same bug - opened a fix upstream: ArtronShop/ArtronShop_RX8130CE#1

Suggested change
buf[4] = bin2bcd(t->tm_wday) & 0x07;
buf[4] = (1 << t->tm_wday) & 0x7F;

t->tm_sec = bcd2bin(buff[0] & 0x7F);
t->tm_min = bcd2bin(buff[1] & 0x7F);
t->tm_hour = bcd2bin(buff[2] & 0x3F);
t->tm_wday = bcd2bin(buff[3] & 0x07);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same bitmap issue as setTime() — the WEEK register is a bitmap, not BCD. This needs to find which bit is set and convert it back to an index. See ArtronShop/ArtronShop_RX8130CE#1

Suggested change
t->tm_wday = bcd2bin(buff[3] & 0x07);
// WEEK register is a bitmap (bit 0=Sun, bit 1=Mon, ..., bit 6=Sat)
uint8_t wday_bits = buff[3] & 0x7F;
t->tm_wday = 0;
while (wday_bits >>= 1) t->tm_wday++;

Comment on lines +46 to +51
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130.begin(&wire);
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: begin() return value is ignored. Unlikely to fail since i2c_probe() already confirmed the device is present, but worth checking defensively to match the pattern of the other RTCs above.

Suggested change
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130.begin(&wire);
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130_success = rtc_8130.begin(&wire);
if (rtc_8130_success) {
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khudson probably good to do this check

Comment on lines +16 to +19
if (!i2c_dev->begin()) {
return false;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the RX8130CE datasheet init flowchart (fig. 42, page 55), after a power loss the VLF flag (bit 1 of Flag Register 0x1D) indicates clock data may be unreliable. A dummy read followed by a VLF check should happen before the register configuration. See also ArtronShop/ArtronShop_RX8130CE#1

Suggested change
if (!i2c_dev->begin()) {
return false;
}
if (!i2c_dev->begin()) {
return false;
}
// Dummy read per init flowchart (page 55)
read_register(0x1D);
// Check VLF flag (bit 1 of Flag Register 0x1D) — indicates data loss
uint8_t flags = read_register(0x1D);
if (flags & 0x02) {
// VLF set: clock data unreliable after power loss, clear flag
write_register(0x1D, flags & ~0x02);
}

#define PIN_VBAT_READ (31) // P0.31 (39) ADC_VBAT
#define PIN_BAT_CHG (34) // P1.02 (26) BAT_CHG_STATUS

#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Meshtastic variant uses an ADC multiplier of 1.667, which was calibrated by simon-muzi via a 15-hour discharge test (Meshtastic commit f4e260e0f). Accounting for nRF52's 3.6V default AREF that works out to 3.6 * 1.667 * 1000 = 6001.2. The current formula gives ~6159 which is about 2.6% off. Could you verify with a multimeter to confirm which value is closer?

Suggested change
#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000)
#define ADC_MULTIPLIER (3.6 * 1.667 * 1000)



bool RTC_RX8130CE::stop(bool stop) {
write_register(0x1E, stop ? 0x040 : 0x00);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
write_register(0x1E, stop ? 0x040 : 0x00);
write_register(0x1E, stop ? 0x40 : 0x00);

Comment on lines +169 to +171
for (int i = 1; i <= len + 1; i++) {
buf[i] = value[i - 1];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (int i = 1; i <= len + 1; i++) {
buf[i] = value[i - 1];
}
for (int i = 1; i <= len; i++) {
buf[i] = value[i - 1];
}

Comment on lines +148 to +153
#define PIN_QSPI_SCK (3) // P0.03 (29) BUZZER
#define PIN_QSPI_CS (26) // P0.26 (34) USER_BUTTON
#define PIN_QSPI_IO0 (30) // P0.30 (33) MCU_SIGNAL
#define PIN_QSPI_IO1 (29) // P0.29 (32) SOFT_SHUTDOWN
#define PIN_QSPI_IO2 (28) // P0.28 (31) BLU_LED_RAK
#define PIN_QSPI_IO3 (2) // P0.02 (30) GPS_PPS
Copy link
Copy Markdown
Contributor

@weebl2000 weebl2000 Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MeshCore convention is PIN_GPS_TX = GPS module's TX output = MCU receives, so PIN_SERIAL1_RX should map to PIN_GPS_TX (and vice versa). Every other nRF52 variant does it that way.

Also, the PIN_QSPI_* block and EXTERNAL_FLASH_* defines should be removed — the R1 Neo has no QSPI flash, and those defines alias active GPIOs (buzzer, user button, soft shutdown, LED, GPS PPS). If the BSP ever initializes the QSPI peripheral it would reconfigure those pins.

Suggested change
#define PIN_QSPI_SCK (3) // P0.03 (29) BUZZER
#define PIN_QSPI_CS (26) // P0.26 (34) USER_BUTTON
#define PIN_QSPI_IO0 (30) // P0.30 (33) MCU_SIGNAL
#define PIN_QSPI_IO1 (29) // P0.29 (32) SOFT_SHUTDOWN
#define PIN_QSPI_IO2 (28) // P0.28 (31) BLU_LED_RAK
#define PIN_QSPI_IO3 (2) // P0.02 (30) GPS_PPS
#define PIN_SERIAL1_RX (PIN_GPS_TX) // MCU receives <- GPS transmits
#define PIN_SERIAL1_TX (PIN_GPS_RX) // MCU transmits -> GPS receives

@brycv
Copy link
Copy Markdown

brycv commented Mar 24, 2026

Where are we at with this? Waiting for more feedback? Thanks again! Can't wait!

@mem101296
Copy link
Copy Markdown

Hello. Wondering what needs to be done to merge this? I'm excited to get the r1 neo on meshcore.

@khudson
Copy link
Copy Markdown
Contributor Author

khudson commented Apr 1, 2026 via email

liamcottle and others added 2 commits April 2, 2026 00:23
Co-authored-by: Wessel <wessel@weebl.me>
Co-authored-by: Wessel <wessel@weebl.me>
Comment on lines +46 to +51
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130.begin(&wire);
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khudson probably good to do this check

#define BATTERY_SAMPLES 8

uint16_t getBattMilliVolts() override {
MESH_DEBUG_PRINTLN("R1Neo: Sampling battery");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably don't need this debug line

@liamcottle
Copy link
Copy Markdown
Member

liamcottle commented Apr 1, 2026

Just waiting for it to be merged at this point. Hopefully @ripplebiz or someone can give it a once over and push it through.

Hey @khudson, thanks for the PR. Sorry for being so slow at reviewing it. Have been working on lots of other things at once. Originally when I looked at it, I saw a bunch of review comments from @weebl2000 that had gone unanswered.

Was waiting to see any movement on those. Most of the changes are variant specific, except for the RTC clock stuff, so if there's any issues it should only affect the new variant.

I added some minor comments, that would be good to resolve, but will merge now anyway so things can move forward :)

Note: I also merged in a couple of minor changes related to the uint8_t vs int8_t for tx power.

Thanks for contributing!

@liamcottle liamcottle merged commit b934daa into meshcore-dev:dev Apr 1, 2026
9 checks passed
@khudson
Copy link
Copy Markdown
Contributor Author

khudson commented Apr 1, 2026 via email

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.

5 participants