diff --git a/README.md b/README.md index e56c575..3caf6e2 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,20 @@ ## About Experimental project around RFM radio modules using an ATmega328P MCU -and [librfm](https://github.com/gitdode/librfm/tree/main). +and [librfm](https://github.com/gitdode/librfm/tree/main)/[librfm95](https://github.com/gitdode/librfm95/tree/main). To do something really extraordinary, the temperature reading of an MCP9808 sensor is periodically transmitted to the receiver. To save battery power, the controller, radio module and temperature sensor are put to power down/sleep mode in between transmissions. The idle current -is ~57 uA, which is still quite a lot (< 10 uA should be possible), but already -better than 8 mA 🙂 +is ~75µA, which is still quite a lot, but already better than 8mA 🙂 +MCU, radio and temp sensor take about 5µA, so the TC1262 3.3V regulator seems +to account for ~70µA. There is basic [SD card support](https://github.com/gitdode/libsdc/tree/main) to test sending larger amounts of data, but it might be useful for something else like a data logger. -![IMG_20250322_012126](https://github.com/user-attachments/assets/e641ddd8-ca52-4149-8d73-3dcee39b7768) +![IMG_20250403_220824](https://github.com/user-attachments/assets/5ad1cdd6-68a3-4055-904f-f2452e3b7b60) The receiver currently converts the raw temperature reading to °C and displays it with the RSSI value, CRC result and transmitter output power on a nice IPS diff --git a/avrrfm.c b/avrrfm.c index c8437e1..cb4bd5e 100644 --- a/avrrfm.c +++ b/avrrfm.c @@ -35,8 +35,8 @@ #include "dejavu.h" #include "unifont.h" -#define TRANSMIT_FAST 1 // 4 ~ 32 seconds -#define TRANSMIT_SLOW 9 // 38 ~ 5 minutes +#define TRANSMIT_FAST 5 // 15 ~ 30 seconds +#define TRANSMIT_SLOW 30 // 150 ~ 5 minutes #define MAX_TIMEOUTS 9 // slow down tx attempts after so many timeouts #define LABEL_OFFSET 10 @@ -44,10 +44,14 @@ #define RED 0xf800 #define WHITE 0xffff +/* Node addresses */ #define NODE0 0x12 #define NODE1 0x24 #define NODE2 0x42 +/* Carrier frequency in kHz */ +#define FREQ 868600 + #ifndef RECEIVER #define RECEIVER 1 #endif @@ -71,6 +75,7 @@ */ ISR(WDT_vect) { watchdogInts++; + rfmTimeout(); } /** @@ -129,6 +134,8 @@ // default fOSC/4 SPCR |= (1 << MSTR); SPCR |= (1 << SPE); + // slow down for the breadboard wiring + spiMid(); } /** @@ -164,8 +171,8 @@ wdt_reset(); // watchdog change enable WDTCSR |= (1 << WDCE) | (1 << WDE); - // enable interrupt, disable system reset, bark every 8 seconds - WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (1 << WDP0); + // enable interrupt, disable system reset, bark every 2 seconds + WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP2) | (1 << WDP1) | (1 << WDP0); } /** @@ -259,7 +266,7 @@ rfmTransmitPayload(payload, sizeof (payload), NODE2); displayTemp(flags.rssi, flags.crc, &temp); - rfmStartReceive(); + rfmStartReceive(false); } /** @@ -296,7 +303,7 @@ printString(buf); _delay_ms(10); - rfmStartReceive(); + rfmStartReceive(false); } /** @@ -340,13 +347,17 @@ sei(); uint8_t node = RECEIVER ? NODE1 : NODE2; - rfmInit(868600, node); + bool radio = rfmInit(FREQ, node); + if (!radio) { + printString("Radio init failed!\r\n"); + } + if (RECEIVER) { initDisplay(); setFrame(WHITE); fillArea(0, 0, DISPLAY_WIDTH, 16, BLACK); // initial rx mode - rfmStartReceive(); + if (radio) rfmStartReceive(false); } while (true) { @@ -354,37 +365,39 @@ // printString("Running...\r\n"); // _delay_ms(1000); - if (!RECEIVER) { - if (watchdogInts % measureInts == 0) { - watchdogInts = 0; + if (radio) { + if (!RECEIVER) { + if (watchdogInts % measureInts == 0) { + watchdogInts = 0; - enableSPI(); - wakeTSens(); - rfmWake(); - if (sdcard) { - transmitData(); - } else { - transmitTemp(NODE1); - bool timeout = waitResponse(); - if (timeout) { - if (++timeoutCount > MAX_TIMEOUTS) { - measureInts = TRANSMIT_SLOW; - timeoutCount = 0; - } + enableSPI(); + wakeTSens(); + rfmWake(); + if (sdcard) { + transmitData(); } else { - timeoutCount = 0; - measureInts = TRANSMIT_FAST; + transmitTemp(NODE1); + bool timeout = waitResponse(); + if (timeout) { + if (++timeoutCount > MAX_TIMEOUTS) { + measureInts = TRANSMIT_SLOW; + timeoutCount = 0; + } + } else { + timeoutCount = 0; + measureInts = TRANSMIT_FAST; + } } + sleepTSens(); + rfmSleep(); + disableSPI(); } - sleepTSens(); - rfmSleep(); - disableSPI(); - } - } else { - PayloadFlags flags = rfmPayloadReady(); - if (flags.ready) { - handlePayload(flags); - // receiveData(flags); + } else { + PayloadFlags flags = rfmPayloadReady(); + if (flags.ready) { + handlePayload(flags); + // receiveData(flags); + } } } diff --git a/librfm.a b/librfm.a index c0c50de..c00f9e1 100644 --- a/librfm.a +++ b/librfm.a Binary files differ diff --git a/librfm.h b/librfm.h index f921684..5d80df9 100644 --- a/librfm.h +++ b/librfm.h @@ -2,7 +2,7 @@ * File: librfm.h * Author: torsten.roemer@luniks.net * - * Created on 23. März 2025, 23:04 + * Created on 30. März 2025 */ #ifndef LIBRFM_H @@ -14,69 +14,74 @@ #define FIFO 0x00 #define OP_MODE 0x01 -#define DATA_MOD 0x02 -#define BITRATE_MSB 0x03 -#define BITRATE_LSB 0x04 -#define FDEV_MSB 0x05 -#define FDEV_LSB 0x06 -#define FRF_MSB 0x07 -#define FRF_MID 0x08 -#define FRF_LSB 0x09 -#define OSC1 0x0a -#define PA_LEVEL 0x11 -#define LNA 0x18 -#define RX_BW 0x19 -#define AFC_FEI 0x1e -#define AFC_BW 0x20 -#define RSSI_CONFIG 0x23 -#define RSSI_VALUE 0x24 -#define DIO_MAP1 0x25 -#define DIO_MAP2 0x26 -#define IRQ_FLAGS1 0x27 -#define RSSI_THRESH 0x29 -#define RX_TO_RSSI 0x2a -#define RX_TO_PRDY 0x2b -#define PREAMB_MSB 0x2c -#define PREAMB_LSB 0x2d -#define IRQ_FLAGS2 0x28 -#define SYNC_CONF 0x2e -#define SYNC_VAL1 0x2f -#define SYNC_VAL2 0x30 -#define SYNC_VAL3 0x31 -#define SYNC_VAL4 0x32 -#define SYNC_VAL5 0x33 -#define SYNC_VAL6 0x34 -#define SYNC_VAL7 0x35 -#define SYNC_VAL8 0x36 -#define PCK_CFG1 0x37 -#define PAYLOAD_LEN 0x38 -#define NODE_ADDR 0x39 -#define CAST_ADDR 0x3a -#define AUTO_MODES 0x3b -#define FIFO_THRESH 0x3c -#define PCK_CFG2 0x3d -#define TEST_LNA 0x58 -#define TEST_PA1 0x5a -#define TEST_PA2 0x5c -#define TEST_DAGC 0x6f -#define TEST_AFC 0x71 +#define BITRATE_MSB 0x02 +#define BITRATE_LSB 0x03 +#define FDEV_MSB 0x04 +#define FDEV_LSB 0x05 +#define FRF_MSB 0x06 +#define FRF_MID 0x07 +#define FRF_LSB 0x08 +#define PA_CONFIG 0x09 +#define PA_RAMP 0x0a +#define OCP 0x0b +#define LNA 0x0c +#define RX_CONFIG 0x0d +#define RSSI_CONFIG 0x0e +#define RSSI_COLLIS 0x0f +#define RSSI_THRESH 0x10 +#define RSSI_VALUE 0x11 +#define RX_BW 0x12 +#define AFC_BW 0x13 +#define AFC_FEI 0x1a +#define PREA_DETECT 0x1f +#define RX_TO_RSSI 0x20 +#define RX_TO_PREA 0x21 +#define RX_TO_SYNC 0x22 +#define RX_DELAY 0x23 +#define OSC 0x24 +#define PREA_MSB 0x25 +#define PREA_LSB 0x26 +#define SYNC_CONFIG 0x27 +#define SYNC_VAL1 0x28 +#define SYNC_VAL2 0x29 +#define SYNC_VAL3 0x2a +#define SYNC_VAL4 0x2b +#define SYNC_VAL5 0x2c +#define SYNC_VAL6 0x2d +#define SYNC_VAL7 0x2e +#define SYNC_VAL8 0x2f +#define PCK_CONFIG1 0x30 +#define PCK_CONFIG2 0x31 +#define PAYLOAD_LEN 0x32 +#define NODE_ADDR 0x33 +#define CAST_ADDR 0x34 +#define FIFO_THRESH 0x35 +#define IMAGE_CAL 0x3b +#define TEMP 0x3c +#define LOW_BAT 0x3d +#define IRQ_FLAGS1 0x3e +#define IRQ_FLAGS2 0x3f +#define DIO_MAP1 0x40 +#define DIO_MAP2 0x41 +#define VERSION 0x42 -#define MASK_MODE 0x1c +#define MASK_MODE 0x07 #define MODE_SLEEP 0x00 -#define MODE_STDBY 0x04 -#define MODE_FS 0x08 -#define MODE_TX 0x0c -#define MODE_RX 0x10 +#define MODE_STDBY 0x01 +#define MODE_FS_TX 0x02 +#define MODE_TX 0x03 +#define MODE_FS_RX 0x04 +#define MODE_RX 0x05 -#define DBM_MIN -2 -#define DBM_MAX 13 -#define PA_MIN 16 -#define PA_MAX 31 -#define PA_OFF 18 +#define DBM_MIN 2 +#define DBM_MAX 17 +#define PA_MIN 0 +#define PA_MAX 15 +#define PA_OFF 2 #define MESSAGE_SIZE 63 -#define F_STEP 6103515625ULL +#define F_STEP 61035 #define CAST_ADDRESS 0x84 /** @@ -97,8 +102,8 @@ void _rfmDelay5(void); /** - * Turns the radio on by pulling its reset pin LOW. - * PORTB &= ~(1 << PB0); + * Turns the radio on by pulling its reset pin high. + * PORTB |= (1 << PB0); */ void _rfmOn(void); @@ -124,9 +129,11 @@ /** * Initializes the radio module with the given carrier frequency in kilohertz - * and node address. + * and node address. Returns true on success, false otherwise. + * + * @return success */ -void rfmInit(uint64_t freq, uint8_t node); +bool rfmInit(uint64_t freq, uint8_t node); /** * Reads interrupt flags. Should be called when any interrupt occurs @@ -135,6 +142,12 @@ void rfmIrq(void); /** + * Sets the "Timeout" interrupt flag, allowing to "unlock" a possibly hanging + * wait for either "PayloadReady" or "Timeout" by the radio. + */ +void rfmTimeout(void); + +/** * Shuts down the radio. */ void rfmSleep(void); @@ -167,9 +180,12 @@ int8_t rfmGetOutputPower(void); /** - * Sets the radio to receive mode and maps "PayloadReady" to DIO0. + * Sets the radio to receive mode and maps "PayloadReady" to DIO0 and enables + * or disables timeout. + * + * @param timeout enable timeout */ -void rfmStartReceive(void); +void rfmStartReceive(bool timeout); /** * Returns true if a "PayloadReady" interrupt arrived and clears the @@ -191,8 +207,8 @@ /** * Waits for "PayloadReady", puts the payload into the given array with the - * given size, and returns the length of the payload, or 0 if a timeout - * occurred. + * given size, enables or disables timeout, and returns the length of the + * payload, or 0 if a timeout occurred. * * @param payload buffer for payload * @param size of payload buffer diff --git a/nbproject/Makefile-Custom.mk b/nbproject/Makefile-Custom.mk index 1c5a827..54aaa32 100644 --- a/nbproject/Makefile-Custom.mk +++ b/nbproject/Makefile-Custom.mk @@ -42,7 +42,6 @@ ${OBJECTDIR}/_ext/48b9ad18/display.o \ ${OBJECTDIR}/_ext/48b9ad18/font.o \ ${OBJECTDIR}/_ext/48b9ad18/i2c.o \ - ${OBJECTDIR}/_ext/48b9ad18/librfm.o \ ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o \ ${OBJECTDIR}/_ext/48b9ad18/rfm.o \ ${OBJECTDIR}/_ext/48b9ad18/sdc.o \ @@ -104,10 +103,6 @@ ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/i2c.o /home/dode/dev/avrrfm/i2c.c -${OBJECTDIR}/_ext/48b9ad18/librfm.o: /home/dode/dev/avrrfm/librfm.c - ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 - $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/librfm.o /home/dode/dev/avrrfm/librfm.c - ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o: /home/dode/dev/avrrfm/mcp9808.c ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o /home/dode/dev/avrrfm/mcp9808.c diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 5464725..4b51237 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -9,7 +9,6 @@ display.c font.c i2c.c - librfm.c mcp9808.c mcp9808.h rfm.c @@ -67,8 +66,6 @@ - - diff --git a/rfm.c b/rfm.c index 469f49e..79f08d7 100644 --- a/rfm.c +++ b/rfm.c @@ -16,7 +16,7 @@ } void _rfmOn() { - PORT_RFM &= ~(1 << PIN_RRST); + PORT_RFM |= (1 << PIN_RRST); } void _rfmSel() { diff --git a/spi.c b/spi.c index 92f51af..af59d1e 100644 --- a/spi.c +++ b/spi.c @@ -1,4 +1,4 @@ -/* +/* * File: spi.c * Author: torsten.roemer@luniks.net * @@ -8,12 +8,21 @@ #include "spi.h" void spiSlow(void) { - SPCR &= ~(1 << SPR0); + // fOSC/64 + SPCR |= (1 << SPR0); SPCR |= (1 << SPR1); SPSR |= (1 << SPI2X); } +void spiMid(void) { + // fOSC/16 + SPCR &= ~(1 << SPR0); + SPCR |= (1 << SPR1); + SPSR &= ~(1 << SPI2X); +} + void spiFast(void) { + // fOSC/4 (default) SPCR &= ~(1 << SPR1) & ~(1 << SPR0); SPSR &= ~(1 << SPI2X); } diff --git a/spi.h b/spi.h index e9c3f66..80f111b 100644 --- a/spi.h +++ b/spi.h @@ -18,6 +18,11 @@ void spiSlow(void); /** + * Sets medium SPI speed. + */ +void spiMid(void); + +/** * Sets fast SPI speed. */ void spiFast(void);