Adafruit RFM69 & Pi Zero high CPU usage

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
jseyfert3
 
Posts: 40
Joined: Mon May 02, 2011 11:48 pm

Adafruit RFM69 & Pi Zero high CPU usage

Post by jseyfert3 »

I'm in the process of creating a network of air/weather sensors, using the RFM69 radio modules. For testing, I currently have an SCD30 CO2 sensor connected to a Feather RFM69 board (32u4). The Feather is also plugged into a 2.9" Grayscale ThinkInk Wing.

My code is very hacked together from sample Adafruit code right now, but it's working fine. Updates the display every 3 minutes (to avoid excessive e-ink updates) and transmits the CO2 reading every 5 seconds via the RFM69.

Code: Select all

/***************************************************
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/

/*  CO2 & Display Test
 *  
 *  Written for an Atmel 32u4 powered, Adafruit Feather board with RFM69 915 MHz radio module, with attached
 *  Adafruit SDC-30 sensor module.
 *  
 *  This program was intended as an introduction to using the AdaFruit ThinkInk 2.9" grayscale E-ink display
 *  (296x128 pixels), and the Adafruit SDC-30 sensor breakout module. The SCD30 sensor is an NDIR CO2 sensor,
 *  which uses IR light and bandwidth limiting filters to measure CO2 concentration by IR absorption,  
 *  compared to a sealed sample of N2 gas for reference absorption and correction of N2 absorption.
 *
 *  To correct for temp and humidity, the SDC-30 CO2 sensor also includes an integrated temp and humidity
 *  sensor chip. These automatically correct the CO2 sensor values before they are read by the microcontroller.
 * 
 *  Specs:
 *    CO2 sensor:
 *       Range: 400 - 10,000 ppm
 *       Accuracy: ±(30 ppm + 3%)
 *       Response Time (τ63%): 20 seconds
 *       Lifetime: 15 years
 *    Relative Humidity sensor:
 *       Range: 0-95% RH
 *       Accuracy: ±3%
 *       Response time (τ63%): 8 seconds
 *    Temperature sensor:
 *       Range: -40 - 70 °C
 *       Accuracy: ±0.4 °C
 *       Response time (τ63%): 10 seconds
 *    SCD30 current consumption: 19 mA at 2 second sample rate (max 75 mA)
 * 
 *  Jonathan Seyfert
 *  2023-02-03
 */

#include "Adafruit_ThinkInk.h"
#include <Adafruit_SCD30.h>

Adafruit_SCD30  scd30;

#define EPD_DC      10 // can be any pin, but required!
#define EPD_CS      9  // can be any pin, but required!
#define EPD_BUSY    -1  // can set to -1 to not use a pin (will wait a fixed delay) <- Available but not connected
#define SRAM_CS     6  // can set to -1 to not use a pin (uses a lot of RAM!)
#define EPD_RESET   -1  // can set to -1 and share with chip Reset (can't deep sleep)

//The following two sections of #ifdef are for the freeMemory() function, to check available free memory for debugging purposes
#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
#else  // __ARM__
extern char *__brkval;
#endif  // __arm__

int freeMemory() {
  char top;
#ifdef __arm__
  return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
  return &top - __brkval;
#else  // __arm__
  return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif  // __arm__
}

// VBATPIN is to measure battery voltage, but that pin is used by E-ink chip select currently, so cannot use.
// #define VBATPIN A9 // Internal battery voltage divider measurement pin

// 2.9" Grayscale Featherwing or Breakout:
ThinkInk_290_Grayscale4_T5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);

#define COLOR1 EPD_BLACK
#define COLOR2 EPD_LIGHT
#define COLOR3 EPD_DARK

const unsigned long updateTime = 180000;  // How often to update display
unsigned long timer = 0;  // Used to check if it's time to update display

// From here to setup() is the stuff for the RFM69 Radiohead Tx demo
// rf69 demo tx rx.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple messageing client
// with the RH_RF69 class. RH_RF69 class does not provide for addressing or
// reliability, so you should only use RH_RF69  if you do not need the higher
// level messaging abilities.
// It is designed to work with the other example rf69_server.
// Demonstrates the use of AES encryption, setting the frequency and modem 
// configuration

#include <SPI.h>
#include <RH_RF69.h>

/************ Radio Setup ***************/

// Change to 434.0 or other frequency, must match RX's freq!
#define RF69_FREQ 915.0

#if defined (__AVR_ATmega32U4__) // Feather 32u4 w/Radio
  #define RFM69_CS      8
  #define RFM69_INT     7
  #define RFM69_RST     4
  #define LED           13
  
#elif defined(ADAFRUIT_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0_EXPRESS) || defined(ARDUINO_SAMD_FEATHER_M0)
  // Feather M0 w/Radio
  #define RFM69_CS      8
  #define RFM69_INT     3
  #define RFM69_RST     4
  #define LED           13
  
#elif defined (__AVR_ATmega328P__)  // Feather 328P w/wing
  #define RFM69_INT     3  // 
  #define RFM69_CS      4  //
  #define RFM69_RST     2  // "A"
  #define LED           13

#elif defined(ESP8266)    // ESP8266 feather w/wing
  #define RFM69_CS      2    // "E"
  #define RFM69_IRQ     15   // "B"
  #define RFM69_RST     16   // "D"
  #define LED           0

#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_NRF52840_FEATHER) || defined(ARDUINO_NRF52840_FEATHER_SENSE)
  #define RFM69_INT     9  // "A"
  #define RFM69_CS      10  // "B"
  #define RFM69_RST     11  // "C"
  #define LED           13

#elif defined(ESP32)    // ESP32 feather w/wing
  #define RFM69_RST     13   // same as LED
  #define RFM69_CS      33   // "B"
  #define RFM69_INT     27   // "A"
  #define LED           13

#elif defined(ARDUINO_NRF52832_FEATHER)
  /* nRF52832 feather w/wing */
  #define RFM69_RST     7   // "A"
  #define RFM69_CS      11   // "B"
  #define RFM69_INT     31   // "C"
  #define LED           17

#endif


/* Teensy 3.x w/wing
#define RFM69_RST     9   // "A"
#define RFM69_CS      10   // "B"
#define RFM69_IRQ     4    // "C"
#define RFM69_IRQN    digitalPinToInterrupt(RFM69_IRQ )
*/
 
/* WICED Feather w/wing 
#define RFM69_RST     PA4     // "A"
#define RFM69_CS      PB4     // "B"
#define RFM69_IRQ     PA15    // "C"
#define RFM69_IRQN    RFM69_IRQ
*/

// Singleton instance of the radio driver
RH_RF69 rf69(RFM69_CS, RFM69_INT);

int16_t packetnum = 0;  // packet counter, we increment per xmission


void setup()
{
  scd30.begin();
  display.begin(THINKINK_GRAYSCALE4);
  delay(5000);  // Pause for 5 seconds to allow sensor time to initialize before displaying inital values
  readAndDisplaySCD30(); // Display initial values on power-up before entering 3 minute update cycle

  // From here to end of setup() is for radiohead tx demo
  Serial.begin(115200);
  //while (!Serial) { delay(1); } // wait until serial console is open, remove if not tethered to computer

  pinMode(LED, OUTPUT);     
  pinMode(RFM69_RST, OUTPUT);
  digitalWrite(RFM69_RST, LOW);

  Serial.println("Feather RFM69 TX Test!");
  Serial.println();

  // manual reset
  digitalWrite(RFM69_RST, HIGH);
  delay(10);
  digitalWrite(RFM69_RST, LOW);
  delay(10);
  
  if (!rf69.init()) {
    Serial.println("RFM69 radio init failed");
    while (1);
  }
  Serial.println("RFM69 radio init OK!");
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module)
  // No encryption
  if (!rf69.setFrequency(RF69_FREQ)) {
    Serial.println("setFrequency failed");
  }

  // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
  // ishighpowermodule flag set like this:
  rf69.setTxPower(20, true);  // range from 14-20 for power, 2nd arg must be true for 69HCW

  // The encryption key has to be the same as the one in the server
  uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
  rf69.setEncryptionKey(key);
  
  pinMode(LED, OUTPUT);

  Serial.print("RFM69 radio @");  Serial.print((int)RF69_FREQ);  Serial.println(" MHz");

}



void loop() 
{
  if(millis() - timer >= updateTime)
  {
    readAndDisplaySCD30();
    timer = millis();
  }

  scd30.read();

  

  char radiopacket[20];
  dtostrf(scd30.CO2, 6, 3, radiopacket);  // needs more work?
//  itoa(packetnum++, radiopacket+13, 10);
  Serial.print("Sending "); Serial.println(radiopacket);
  
  // Send a message!
  rf69.send((uint8_t *)radiopacket, strlen(radiopacket));
  rf69.waitPacketSent();

  delay(5000);
}

void readAndDisplaySCD30()
{
  scd30.read();

  // float measuredvbat = analogRead(VBATPIN);
  // measuredvbat *= 2;    // we divided by 2, so multiply back
  // measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
  // measuredvbat /= 1024; // convert to voltage

  display.clearBuffer();
  display.setTextSize(2);
  display.setTextColor(EPD_BLACK);
  display.setCursor(5, 5);
  display.print(F("Temp: "));
  display.print(scd30.temperature);
  display.print(F(" Celcius"));
  
  display.setCursor(5, 30);
  display.print(F("RH: "));
  display.print(scd30.relative_humidity);
  display.print(F(" %"));

  display.setCursor(5, 55);
  display.print(F("CO2: "));
  display.print(scd30.CO2, 3);
  display.print(F(" ppm"));

  display.setCursor(5, 80);
  display.print(F("Free Memory: "));
  display.print(freeMemory());  
  display.print(F(" bytes"));

  //char *textDisplay[] = {""};

  display.setCursor(0, 100);
  display.setTextSize(1);
  display.setTextWrap(true);
  display.print(F("This display updates every 3 minutes. E-ink display uses no power except when updating. If sensor board has green LED lit, system is running."));

  display.display();
}
And then on the receiving end is a Raspberry Pi Zero W with the RFM69 and OLED hat. Receiving for this test was almost exclusively a stock radio_frm69.py, only think I added was two "print()" calls to show the CO2 packet and RSSI.

Code: Select all

# SPDX-FileCopyrightText: 2018 Brent Rubell for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
Example for using the RFM69HCW Radio with Raspberry Pi.

Learn Guide: https://learn.adafruit.com/lora-and-lorawan-for-raspberry-pi
Author: Brent Rubell for Adafruit Industries
"""
# Import Python System Libraries
import time
# Import Blinka Libraries
import busio
from digitalio import DigitalInOut, Direction, Pull
import board
# Import the SSD1306 module.
import adafruit_ssd1306
# Import the RFM69 radio module.
import adafruit_rfm69

# Button A
btnA = DigitalInOut(board.D5)
btnA.direction = Direction.INPUT
btnA.pull = Pull.UP

# Button B
btnB = DigitalInOut(board.D6)
btnB.direction = Direction.INPUT
btnB.pull = Pull.UP

# Button C
btnC = DigitalInOut(board.D12)
btnC.direction = Direction.INPUT
btnC.pull = Pull.UP

# Create the I2C interface.
i2c = busio.I2C(board.SCL, board.SDA)

# 128x32 OLED Display
reset_pin = DigitalInOut(board.D4)
display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c, reset=reset_pin)
# Clear the display.
display.fill(0)
display.show()
width = display.width
height = display.height

# Configure Packet Radio
CS = DigitalInOut(board.CE1)
RESET = DigitalInOut(board.D25)
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, 915.0)
prev_packet = None
# Optionally set an encryption key (16 byte AES key). MUST match both
# on the transmitter and receiver (or be set to None to disable/the default).
rfm69.encryption_key = b'\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08'

while True:
    packet = None
    # draw a box to clear the image
    display.fill(0)
    display.text('RasPi Radio', 35, 0, 1)

    # check for packet rx
    packet = rfm69.receive()
    if packet is None:
        display.show()
        display.text('- Waiting for PKT -', 15, 20, 1)
    else:
        # Display the packet text and rssi
        display.fill(0)
        prev_packet = packet
        packet_text = str(prev_packet, "utf-8")
        display.text('RX: ', 0, 0, 1)
        display.text(packet_text, 25, 0, 1)
        print(packet_text)
        print(rfm69.rssi)
        time.sleep(1)

    if not btnA.value:
        # Send Button A
        display.fill(0)
        button_a_data = bytes("Button A!\r\n","utf-8")
        rfm69.send(button_a_data)
        display.text('Sent Button A!', 25, 15, 1)
    elif not btnB.value:
        # Send Button B
        display.fill(0)
        button_b_data = bytes("Button B!\r\n","utf-8")
        rfm69.send(button_b_data)
        display.text('Sent Button B!', 25, 15, 1)
    elif not btnC.value:
        # Send Button C
        display.fill(0)
        button_c_data = bytes("Button C!\r\n","utf-8")
        rfm69.send(button_c_data)
        display.text('Sent Button C!', 25, 15, 1)

    display.show()
    time.sleep(0.1)
What worries me though, is that monitoring "top" on the Pi, and running the above Python script, is that Python is using 50-75% of the CPU essentially continuously. I was hoping once everything was set up for a network of remote sensors and they were all feeding data back to the Pi, and the Pi was logging this data, that I could eventually add in a webserver so I can view the data on the Pi from my phone/etc. But if just sitting there acting as a data receiver is using so much of the CPU on the Pi Zero, I'm thinking that's going to put a kink in plans on running a webserver too.

I noticed reading the documentation that:
This is a ‘best effort’ at receiving data using pure Python code–there is not interrupt support so you might lose packets if they’re sent too quickly for the board to process them. You will have the most luck using this in simple low bandwidth scenarios like sending and receiving a 60 byte packet at a time–don’t try to receive many kilobytes of data at a time!
If I'm understanding that correctly, between the "pure Python code" and "no interrupt support" I'm guessing it's always going to use a bit of CPU? I know Python, being an interpreted language, is not very efficient.

Essentially, I'm looking for suggestions. Things I have thought of as possibilities:
  • Continue to try as-is, and see what happens.
  • Try to optimize the existing Adafruit Python script for lower CPU usage without affecting ability to receive.
  • Locate a different RFM69 library written in C++ for faster performance.
  • Attempt to write my own library, or at least program, in C++ to run on the Pi for faster performance (this would be ambitious of me to try at this point).
  • Don't use the Pi for receiving, but use another microcontroller, buffer the received packets, and have the Pi grab these buffered packets at a slow rate (like once a minute).
  • Use a faster Pi for receiving. I'm certainly not opposed to this, but the Pi Zero W is the only Pi I have, and looks like it'll be the middle of the year before production is high enough I can get my hands on a Pi 3 or Pi 4. Plus currently it appears there's 1-per-customer limits on all Pis, and I was hoping to set up a Pi as a portable programming computer, so if limited to 1-per-customer, my 1 would go towards that. So essentially using a faster Pi as the base station is an option, but only once supplies recover to allow easy access to purchasing multiple Pis.
Other suggestions welcome.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by adafruit_support_mike »

Add the webserver and see what effect it has on CPU load. I doubt there will be much difference.

The fact that Python has to poll the radio module means you'll see a high reported load, but also take a look at the status of the Python interpreter in the process listing. I'd guess it will usually be BIOWAIT, which means 'waiting on basic IO'. In other words, the CPU isn't really doing much, but the Python interpreter says, "oh yeah.. I'm working" every time the kernel checks its activity.

User avatar
Rcayot
 
Posts: 321
Joined: Sat Feb 08, 2020 6:48 pm

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by Rcayot »

I would eliminate the button portion of the code until you have everything else sorted.

Next I would use a different approach to handling the polling of the radio. The use of sleep(0.1) means that the program is checking the radio output and everything else every 0,1 seconds.

Check out the tutorials that cover multitasking in CP, or look at asyncio which is cooperative multitasking.

I have a similar set up, though I use CP on a RPi PICO with an RF96x radio, instead of Arduino or micropython.

Roger

User avatar
jseyfert3
 
Posts: 40
Joined: Mon May 02, 2011 11:48 pm

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by jseyfert3 »

adafruit_support_mike wrote: Tue Feb 07, 2023 11:01 pm Add the webserver and see what effect it has on CPU load. I doubt there will be much difference.

The fact that Python has to poll the radio module means you'll see a high reported load, but also take a look at the status of the Python interpreter in the process listing. I'd guess it will usually be BIOWAIT, which means 'waiting on basic IO'. In other words, the CPU isn't really doing much, but the Python interpreter says, "oh yeah.. I'm working" every time the kernel checks its activity.
In top, the python process reports as "R" (running) most of the time, and occasionally "S".
Rcayot wrote: Wed Feb 08, 2023 9:16 am I would eliminate the button portion of the code until you have everything else sorted.

Next I would use a different approach to handling the polling of the radio. The use of sleep(0.1) means that the program is checking the radio output and everything else every 0,1 seconds.

Check out the tutorials that cover multitasking in CP, or look at asyncio which is cooperative multitasking.

I have a similar set up, though I use CP on a RPi PICO with an RF96x radio, instead of Arduino or micropython.

Roger
I got rid of the button code and any updating of the display outside of the if/then statements, and did nothing if a packet was not received.

Code: Select all

while True:
    packet = None

    # check for packet rx
    packet = rfm69.receive()
    if packet is None:
        pass      
    else:
        # Display the packet text and rssi
        display.fill(0)
        prev_packet = packet
        packet_text = str(prev_packet, "utf-8")
        display.text('RX: ', 0, 0, 1)
        display.text(packet_text, 25, 0, 1)
        print(packet_text)
        print(rfm69.rssi)
        display.show()
        time.sleep(1)
        display.text('- Waiting for PKT -', 15, 20, 1)
        display.show()

    time.sleep(0.1)
Interestingly, this actually used more CPU, up around 75%.

Then I changed time.sleep to 5 seconds. And CPU usage of the python process dropped to single digits, on average. And I did not miss any packets, which are currently being sent every 15 seconds.

However, that isn't a long term solution. From reading the datasheet for the radio chip, it stores received data in a FIFO buffer. 66 bytes long, I believe. If you don't read this before new data comes in, it's pushed out (or you don't receive the new data? I'm not completely sure). So if I have a network of, say, 10 sensors, transmitting asynchronously every minute, sometimes you will get sensors reporting back to back and a 5 second sleep could easily miss packets.

Now, I agree with you a different method of checking for data instead of sleep() is best. If I understand the datasheet, the radio chip also sets one of it's GPIO pins HIGH when the FIFO contains data, and LOW when it doesn't. If so, this is perfect for interrupts. The code doesn't have to poll the radio ALL...just set up an interrupt for when the FIFO has data. And pull the data off using the interrupt.

But, this does lead to the fact the library says it is a "best effort...without interrupts...may miss data...not for rapid data".

So all of this would lead to either re-writing the library, or finding a different library that already did this.

User avatar
jerryn
 
Posts: 1868
Joined: Sat Sep 14, 2013 9:05 am

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by jerryn »

There is an example for using interrupts on a Raspberry Pi when receiving https://github.com/adafruit/Adafruit_Ci ... terrupt.py

Your mileage may vary ;-)
Good luck!

User avatar
jseyfert3
 
Posts: 40
Joined: Mon May 02, 2011 11:48 pm

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by jseyfert3 »

jerryn wrote: Wed Feb 08, 2023 12:36 pm There is an example for using interrupts on a Raspberry Pi when receiving https://github.com/adafruit/Adafruit_Ci ... terrupt.py

Your mileage may vary ;-)
Good luck!
I was thinking after I wrote the above post that I could probably use the library as-is, and just call receive() when there was an interrupt from the RFM69, once I figured out how to use interrupts and what pin on the RFM69 would be needed for that. And that is exactly what this example code is doing!

I didn't think to check the examples because the library README clearly says:
Note

This is a 'best effort' at receiving data using pure Python code--there is not interrupt support so you might lose packets if they're sent too quickly for the board to process them. You will have the most luck using this in simple low bandwidth scenarios like sending and receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time!
I see now it's probably because they are referring mainly to using the RFM69 with CircuitPython on a microcontroller, like the M0 Feather with RFM69, per the Raspberry Pi interrupt code comments:
# Example using Interrupts to send a message and then wait indefinitely for messages
# to be received. Interrupts are used only for receive. sending is done with polling.
# This example is for systems that support interrupts like the Raspberry Pi with "blinka"
# CircuitPython does not support interrupts so it will not work on Circutpython boards
So thank you again for pointing this out! I think this is pretty much exactly what I need. Or at least a great starting point for further testing. I've used interrupts on an Arduino before, to ensure you didn't miss input button presses because the code was in the middle of calculating a number that took longer than the average button press, but haven't yet used them on the Pi. I'll give this example code a spin tonight.

User avatar
Rcayot
 
Posts: 321
Joined: Sat Feb 08, 2020 6:48 pm

Re: Adafruit RFM69 & Pi Zero high CPU usage

Post by Rcayot »

for receiving packets and sending oon to adafruit IO, I use the following:

Code: Select all

# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries
# SPDX-License-Identifier: MIT
# modified by Roger Ayotte 11/12/2021
# Example to receive addressed packed with ACK and send a response

import time
import board
import busio
import digitalio
import adafruit_rfm9x
from gpiozero import PingServer
adafruit=PingServer('adafruit.com', event_delay=1.0)
adafruit=PingServer('adafruit.com')

# Define radio parameters.
RADIO_FREQ_MHZ = 915.0  # Frequency of the radio in Mhz. Must match your
# module! Can be a value like 915.0, 433.0, etc.

# Define pins connected to the chip.
# set GPIO pins as necessary - this example is for Raspberry Pi
CS = digitalio.DigitalInOut(board.CE1)
RESET = digitalio.DigitalInOut(board.D25)

# Initialize SPI bus.
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
# Initialze RFM radio
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ, baudrate = 500000, crc = True)
rfm9x.coding_rate = 8
# Optionally set an encryption key (16 byte AES key). MUST match both
# on the transmitter and receiver (or be set to None to disable/the default).
#rfm69.encryption_key = (
#    b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"
#)

# set delay before transmitting ACK (seconds)
rfm9x.ack_delay = 0.1
# set node addresses
rfm9x.node = 2
rfm9x.destination = 1
# initialize counter
counter = 0
ack_failed_counter = 0
tag = 0
from Adafruit_IO import Client, Feed, Data, RequestError
import datetime

# Set to your Adafruit IO key.
ADAFRUIT_IO_KEY = 'mykey'
# Set to your Adafruit IO username.
ADAFRUIT_IO_USERNAME = 'myname'
# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
# Set up Adafruit IO Feeds.
# first test if internet is reachable
if adafruit.is_active:
    temperature_feed = aio.feeds('temperature')
    humidity_feed = aio.feeds('humidity')
    pressure_feed = aio.feeds('pressure')
    voltage_feed = aio.feeds('voltage')
    wind_feed = aio.feeds('wind')
    gust_feed = aio.feeds('gust')
    lightning_feed = aio.feeds('lightning')
else:
    print("wifi not ready")
# Wait to receive packets.
print("Waiting for packets...")

while True:
    try:
        # Look for a new packet: only accept if addresses to my_node
        packet = rfm9x.receive(with_ack=True, with_header=True)
        # If no packet was received during the timeout then None is returned.
        if packet is not None:
            payload = str(packet[4:], "utf-8")
            tag = int(payload[0])
            if tag == 1:
                temperature = payload[1:]
                temperature = (float(temperature)*1.8 + 32)
                temperature=round(temperature, 2)
                print("temperature = ", temperature, "F")
                if adafruit.is_active:
                    aio.send(temperature_feed.key, str(temperature))
                else:
                    print("wifi not ready")
            elif tag == 3:
                pressure = payload[1:]
                print("pressure = ", pressure, "mB")
                if adafruit.is_active:
                    aio.send(pressure_feed.key, str(pressure))
                else:
                    print("wifi not ready")
            elif tag == 2:
                humidity = payload[1:]
                print("humidity = ", humidity, "%")
                if adafruit.is_active:
                    aio.send(humidity_feed.key, str(humidity))
                else:
                    print("wifi not ready")
            elif tag == 4:
                voltage = payload[1:]
                print("voltage = ", voltage)
                if adafruit.is_active:
                    aio.send(voltage_feed.key, str(voltage))
                else:
                    print("wifi not ready")
            elif tag == 5:
                wind = payload[1:]
                print("wind speed = ", wind , " m/s")
                if adafruit.is_active:
                    aio.send(wind_feed.key, str(wind))
                else:     
                    print("wifi not ready")
            elif tag == 6:
                gust = payload[1:]
                print("gust = ", gust, " m/s")
                if adafruit.is_active:
                    aio.send(gust_feed.key, str(gust))
                else:
                    print("wifi not ready")
            elif tag == 7:
                fan_status = payload[1:]
                print("fan status = ", fan_status)
            elif tag == 8:
                fan_speed = payload[1:]
                print("fan speed = ", fan_speed)
            elif tag == 9:
                lightning = payload[1:]
                print("lightning event detected", lightning)
                if adafruit.is_active:
                    aio.send(lightning_feed.key, str(lightning))
                else:
                    print("wifi not ready")
                
    except (UnicodeDecodeError, RequestError) as exception:
        print("error detected")
    finally:
        continue
This takes 23% of my Pi-4B processor time. note there is no sleep function, it polls constantly. I also check for the presence of a wifi connection before every send because if the wifi is down for some reason it will crash the program.

I use a 'flag' I place into teh packet ahead fo teh data string because the flag functio0n in the header is not 100% reliable. I have 9 different data strings I send, thisd is a very robust program.

Good luck,
Roger

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”