diff --git a/README.md b/README.md index 4d0defa..db35b64 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Experimental project to drive an RFM69HCW radio module with plain avr-libc and an Atmega328p MCU. -This is work in progress. Simple Tx-Rx is working so far. +This is work in progress. Simple Tx-Rx with response is working so far. I'm impressed how well these radio modules work; the range achieved with simple wire antennas as well as the reliable packet transmission. @@ -18,8 +18,12 @@ ![IMG_20250212_190518](https://github.com/user-attachments/assets/dd87b7de-c97d-4ecb-ab24-f5a34b849914) -The receiver currently just converts the raw temperature reading to °C and +The receiver currently converts the raw temperature reading to °C and displays it with the RSSI value and CRC result on a nice IPS TFT display. +It responds to the transmitter as kind of ack with the RSSI, which +might be useful do to some sort of power management in the transmitter. +The transmitter waits for this response with a timeout so it won't be blocked +and comsume a lot of power just because there is no response coming back. ## Range diff --git a/avrrfm.c b/avrrfm.c index 2810e40..0efbdf2 100644 --- a/avrrfm.c +++ b/avrrfm.c @@ -34,18 +34,26 @@ #include "unifont.h" #define MEASURE_INTS 4 +#define TIMEOUT_INTS 30 // about one second + #define LABEL_OFFSET 10 #define BLACK 0x0000 #define RED 0xf800 #define WHITE 0xffff +#define NODE1 0x24 +#define NODE2 0x42 + #ifndef RECEIVER #define RECEIVER 1 #endif /* 1 int = 8 seconds */ -static volatile uint8_t ints = 0; +static volatile uint8_t wdints = 0; + +static volatile bool toena = false; +static volatile uint8_t toints = 0; /* Temp. label coordinates */ static x_t xl = 0; @@ -55,7 +63,15 @@ static width_t width = 0; ISR(WDT_vect) { - ints++; + wdints++; +} + +ISR(TIMER0_COMPA_vect) { + if (toena && toints++ >= TIMEOUT_INTS) { + toints = 0; + toena = false; + timeoutRadio(); + } } /** @@ -129,6 +145,20 @@ } /** + * Sets up the timer. + */ +static void initTimer(void) { + // timer0 clear timer on compare match mode, TOP OCR0A + TCCR0A |= (1 << WGM01); + // timer0 clock prescaler/1024/255 ~ 46 Hz @ 12 MHz ~ 61 Hz @ 16 MHz + TCCR0B |= (1 << CS02) | (1 << CS00); + OCR0A = 255; + + // enable timer0 compare match A interrupt + TIMSK0 |= (1 << OCIE0A); +} + +/** * Enables SPI. */ static void enableSPI(void) { @@ -144,16 +174,31 @@ } /** - * Reads the temperature from the sensor and transmits it. + * Reads the temperature from the sensor and transmits it with the given + * node address. */ -static void transmitTemp(void) { +static void transmitTemp(uint8_t node) { uint16_t temp = readTSens(); uint8_t payload[] = {(temp >> 8), temp & 0x00ff}; - transmitPayload(payload, sizeof (payload)); + transmitPayload(payload, sizeof (payload), node); // printString("Transmitted\r\n"); } /** + * Waits for a response from the receiver with timeout. + */ +static void waitResponse(void) { + toena = true; + uint8_t response[1]; + int8_t len = receivePayload(response, sizeof (response)); + if (len > 0) { + // receiver RSSI + int8_t rssi = divRoundNearest(response[0], 2); + printUint(rssi); + } +} + +/** * Converts the raw temperature to °C and lets it float around the display. * * @param raw temperature @@ -208,6 +253,7 @@ if (!RECEIVER) { // used only for tx initWatchdog(); + initTimer(); } // enable global interrupts @@ -215,12 +261,11 @@ printString("Hello Radio!\r\n"); - initRadio(868600); + uint8_t node = RECEIVER ? NODE1 : NODE2; + initRadio(868600, node); if (RECEIVER) { initDisplay(); - - setFrame(0xffff); - + setFrame(WHITE); // initial rx mode startReceive(); } @@ -231,13 +276,14 @@ // _delay_ms(1000); if (!RECEIVER) { - if (ints % MEASURE_INTS == 0) { - ints = 0; + if (wdints % MEASURE_INTS == 0) { + wdints = 0; enableSPI(); wakeTSens(); wakeRadio(); - transmitTemp(); + transmitTemp(NODE1); + waitResponse(); sleepTSens(); sleepRadio(); disableSPI(); @@ -247,6 +293,12 @@ if (flags.ready) { uint8_t rssi = readRssi(); uint16_t raw = readTemp(); + + // TODO delay? + _delay_ms(10); + uint8_t payload[] = {rssi}; + transmitPayload(payload, sizeof (payload), NODE2); + displayTemp(rssi, flags.crc, raw); startReceive(); } diff --git a/rfm69.c b/rfm69.c index 658289f..5ad3515 100644 --- a/rfm69.c +++ b/rfm69.c @@ -74,7 +74,7 @@ // printString("irq\r\n"); } -void initRadio(uint64_t freq) { +void initRadio(uint64_t freq, uint8_t node) { // wait a bit after power on _delay_ms(10); @@ -125,7 +125,7 @@ regWrite(RX_BW, 0x54); // RX_BW during AFC (default 0x8b) - regWrite(AFC_BW, 0x8a); + regWrite(AFC_BW, 0x54); // AFC auto on // regWrite(AFC_FEI, 0x04); @@ -171,7 +171,7 @@ regWrite(PCK_CFG2, 0x00); // node and broadcast address - regWrite(NODE_ADDR, NODE_ADDRESS); + regWrite(NODE_ADDR, node); regWrite(CAST_ADDR, CAST_ADDRESS); // set TX start condition to "at least one byte in FIFO" @@ -183,6 +183,10 @@ printString("Radio init done\r\n"); } +void timeoutRadio(void) { + irqFlags1 |= (1 << 2); +} + void sleepRadio(void) { setMode(MODE_SLEEP); } @@ -230,27 +234,34 @@ } spiDes(); + // FIXME this is not the actual length of the payload received return len; } size_t receivePayload(uint8_t *payload, size_t size) { startReceive(); - loop_until_bit_is_set(irqFlags2, 2); + // wait until "PayloadReady" or timeout + do {} while (!(irqFlags2 & (1 << 2)) && !(irqFlags1 & (1 << 2))); + bool timeout = irqFlags1 & (1 << 2); clearIrqFlags(); setMode(MODE_STDBY); + if (timeout) { + return 0; + } + return readPayload(payload, size); } -size_t transmitPayload(uint8_t *payload, size_t size) { +size_t transmitPayload(uint8_t *payload, size_t size, uint8_t node) { // payload + address byte size_t len = min(size, FIFO_SIZE) + 1; spiSel(); transmit(FIFO | 0x80); transmit(len); - transmit(NODE_ADDRESS); + transmit(node); for (size_t i = 0; i < size; i++) { transmit(payload[i]); } diff --git a/rfm69.h b/rfm69.h index 09fba85..e6b2d96 100644 --- a/rfm69.h +++ b/rfm69.h @@ -75,13 +75,18 @@ #define FIFO_SIZE 64 #define F_STEP 6103515625ULL -#define NODE_ADDRESS 0x42 #define CAST_ADDRESS 0x84 /** - * Initializes the radio module with the given carrier frequency in kilohertz. + * Initializes the radio module with the given carrier frequency in kilohertz + * and node address. */ -void initRadio(uint64_t kHz); +void initRadio(uint64_t freq, uint8_t node); + +/** + * Indicates a timeout. + */ +void timeoutRadio(void); /** * Shuts down the radio. @@ -117,28 +122,32 @@ * Sets the radio in standby mode, puts the payload into the given array * with the given size, and returns the length of the payload. * - * @param payload - * @param size - * @return actual length of the payload + * @param payload buffer for payload + * @param size of payload buffer + * @return payload bytes actually received */ size_t readPayload(uint8_t *payload, size_t size); /** * Waits for "PayloadReady", puts the payload into the given array with the - * given size, and returns the length of the payload. + * given size, and returns the length of the payload, or 0 if a timeout + * occurred. * * @param payload buffer for payload - * @return bytes actually received + * @param size of payload buffer + * @return payload bytes actually received */ size_t receivePayload(uint8_t *payload, size_t size); /** - * Transmits up to 64 bytes of the given payload. + * Transmits up to 64 bytes of the given payload with the given node address. * * @param payload to be sent - * @return bytes actually sent + * @param size of payload + * @param node address + * @return payload bytes actually sent */ -size_t transmitPayload(uint8_t *payload, size_t size); +size_t transmitPayload(uint8_t *payload, size_t size, uint8_t node); #endif /* RFM69_H */