Skip to content

Muzi Works R1 Neo support#2007

Open
khudson wants to merge 2 commits intomeshcore-dev:devfrom
khudson:r1neo
Open

Muzi Works R1 Neo support#2007
khudson wants to merge 2 commits intomeshcore-dev:devfrom
khudson:r1neo

Conversation

@khudson
Copy link
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
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
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
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
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");
}
}

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

Copy link
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
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)

radio.setCodingRate(cr);
}

void radio_set_tx_power(uint8_t dbm) {
Copy link
Contributor

Choose a reason for hiding this comment

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

RadioLib's setOutputPower() takes int8_t. Minor, but other variants in the project use int8_t here too.

Suggested change
void radio_set_tx_power(uint8_t dbm) {
void radio_set_tx_power(int8_t dbm) {

bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
Copy link
Contributor

Choose a reason for hiding this comment

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

Matching signature fix.

Suggested change
void radio_set_tx_power(uint8_t dbm);
void radio_set_tx_power(int8_t dbm);



bool RTC_RX8130CE::stop(bool stop) {
write_register(0x1E, stop ? 0x040 : 0x00);
Copy link
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
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
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

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.

2 participants