Connecting Pico Display Pack with RFM69HCW Transceiver

I am currently using two RFM69HCW transceivers (ISM) for a remote weather station. The current display is a OLED 128x64, but the characters are quite small. So, I decided to try the Pico Display Pack 2.8, 320x240. Plenty of real estate for the numbers now. However, I am a bit vague as to which pins are not being used by the display pack, so I can use them for the transceiver. I need 6 pins for DI0-DI5, plus 5 additional pins for RX, TX, CS, SCK and RST (for xcvr). Are there that many unused pins available in the Display Pack 2? And can the RGB pins be used if necessary.
BTW, the color display is fantastic, but very tight when used on a dual expander board.

You can check the free pins in the schematic from the shop. You can share the RX, TX, SCK pins with the display btw. if you have pullups on your CS lines.

Also, do you really need DI0 - DI5? You should check, since I can operate my RFM69 without any these pins. But this depends of course on the driver you use.

Thank you for the reply. In my case, I am using Packet mode with AES and Sync. So, the DI0-4 pins are used for RSSI, TxReady, RxReady, Data, PacketSent (in no particular order). I may not be using DI5 as I have no use for ClkOut. The libraries I am using are modified from LowPowerLab, as the RadioHead libraries seemed rather broad for my narrow purpose. However, it would be desirable to connect all the pins for more mapping flexibility. The only schematic I found, dated 07/10/2021, is labelled for “Display Pack 2.0” but still shows the LED pins on Pico pins 9-11. Is there a newer schematic?

There is some info here: Pico Display Pack 2.8 info?. According to this post the pins for the LEDs have moved.

If you look at the back of the Display-Pack 2.8, you can see “LED CUT” and some solder traces you can cut. So once you take out your knife you should be able to repurpose those GPIOs.

The product page mentions the new pins used. I’m not sure if it did when I started that thread but it is mentioned now.
Note that the RGB LED is hooked up to different pins on 2.8" (26, 27 and 28) so you may need to adjust for this in your code.

Thank you both for the updated info. I did find the new schematic, which indicates the new LED pin-outs, and cutting. It seems I may have to be creative in selecting pins for the transceiver. Sounds like fun!
If I manage to get this project working with the new display, I may post additional info here, if it may be useful. Have a great day!

1 Like

I have two Display Pack 2.8’s wired up side by side on a weather station build. It’s the data display end. It’s been a work in progress for what seems like forever. All was fine with an RP2040, but I had to add 10k pullups to the CS lines to get them to work with an RP2350.
Code runs fine from Thonny, but not as main.py - Support - Pimoroni Buccaneers

Unless I can determine how to use the same SPI pins as the LCD uses, I have discovered there are just not enough free pins to use the RFM69HCW, with DI0-4. Unless someone has some code snippets that have accomplished that (multiplexing?). Or some fresh ideas…
I am working on getting two picos talking to each other, but a problem exists on how to recover the sent message in ‘main()’ without slowing down the interrupt routines. There is a LOT of code to run the transceivers.

The Pico Plus 2 has some extra GPIO on the SP/CE connector. It uses the RP2350B.
Pimoroni Pico Plus 2
8 Pin JST-SH Cable (SP/CE) – JST-SH to DuPont sockets (pimoroni.com)
Or maybe the PGA2350, its also a B. It’s a bit of work wiring it up though.
PGA2350 (pimoroni.com)

I have abandoned the i2c approach as I have had a real time getting the complete messages to come through on the receiver. Two days is enough time for that effort. Now I am using modified code from the SDK’s pico-examples UART section which uses PIO. That is now working beautifully, and the code is smaller than the i2c code I was using. The next step is to integrate that bit of code into the RMF69HCW receiver code. So, the effort will use two Picos, the existing transceiver Pico and the Pico on the back of the Display Pack 2.8.
The adventure continues…

It seems there is always something that goes sideways in what should be a pretty simple operation. I have got the RFM69HCW transceiver receiving the message from the transmitter station. What I now need to do is send that message on to the Pico mounted to the Pico Display Pack 2.8. I am using UART to do that. Now the problem seems to be the format of the received message causing transmission issues. Go figure!
I will show the code if I can. The uart_tx is hiccuping on sending the message.

using namespace std;
vector<uint8_t> rcvdMessage;
int main() {
    ...
    uint8_t val = 0;
    while(1) { // use while(!val) for single packet reception
        val = receive(); // should return on 'packet_ready' val in function
        // 1st byte of received packet (variable length) is Fifo length
        ...
        // rcvdMessage is global variable, vector<uint8_t>. 
        string s(rcvdMessage.begin(), rcvdMessage.end()); // convert vector<uint8_t> to string
        std::cout << "\nReceived message:\n" << s << std::endl; // show message, WORKS   
        // Send out a string, with CR/LF conversions, TEST
        string str = "Hello, this is a test\n"; // TEST
        char* c = &*s.begin();
       uart_puts(UART_ID, c); // send message out UART TX (GP0) each 'receive()' cycle
    }
    ...
}

Now the part that has me befuddled is the above “str” message will go fine, even without the “c” pointer, but the “s” message refuses to yield anything when applied to the “uart_puts” command. Also, the “cout” line sends to the console the correct message received. I have tried a bunch of conversions for the UART, listed below, to no avail.

//char* c = const_cast<char*>(s.c_str());
//char* c = strcpy(new char[s.length() + 1], s.c_str());
    
//int len = rcvdMessage.size();
//char *c = new char[len + 1];
//std::copy(rcvdMessage.begin(), rcvdMessage.end(), c);
//c[len] = '\0';

//std::vector<char> chars(rcvdMessage.begin(), rcvdMessage.end());
//chars.push_back('\0');
//char *c = &chars[0];

The received message is simple, recreated sample below.

vector<uint8_t> rcvdMessage = {0x02, 0x00, 0x00, 0x54, 0x3a, 0x20, 0x36, 0x32, 0x20, 0x46, 0x0a, 0x48, 0x3a, 0x20, 0x32, 0x39, 0x2e, 0x32, 0x20, 0x25, 0x0a, 0x50, 0x3a, 0x20, 0x32, 0x33, 0x2e, 0x32, 0x32, 0x20, 0x69, 0x6e, 0x48, 0x67, 0x0a, 0x1d, 0x1d};

So, that’s where I am now. Any clues or concrete suggestions would be most welcome. BTW, I am in no way a C++ expert, or even on good speaking terms with C++, and I’m sure the code is pretty ugly. I just like to dabble a bit. “R” is my other favorite.

Do you read from uart in byte mode or in line mode? Your message does not seem to be terminated by a CRLF and your code does not add this terminator.

BTW: “R” is great ;-)

This is where I am now. Have the two picos talking, one sending the message as in earlier comments, the other receiving the message. The second is connected to the pico display pack 2.8.

while(true) {
    //vector<uint8_t> buffer;
   . . .
    // Read and store in a buffer.
    while((c=uart_rx_program_getc(pio, sm)) !=EOF) { // Works, never exits
        //buffer.push_back(c);
        putchar(c);
        i++;
    } 
   . . .
}

The above shows the correct message on the console received from the sending Pico. The issue is breaking out of the second while() loop to continue processing the received message as shown below. There doesn’t seem to be an ‘EOF’ or ‘Ctrl-D’.

    std::string s(buffer.begin(), buffer.end()); // convert vector<uint8_t> to string
    auto posStartT = s.find('T'); // temperature substring start
    auto posStartH = s.find('H'); // humidity substring start
    auto posStartP = s.find('P'); // pressure substring start
    
    // substr(x,y): x = start character, y = number of characters to get
    string Temp = s.substr(posStartT+3,3); // retrieve digits and count
    string Humid = s.substr(posStartH+3,2); // retrieve digits and count
    string Press = s.substr(posStartP+3,5); // retrieve digits and count

If I can find a safe way to break from the loop, I can then determine if the ‘.push_back()’ line will work. Eventually, I will process the data for the pico display pack 2.8 as shown below.

    graphics.set_pen(BG); // background
    graphics.clear();
    // Readings text and unit of measure
    graphics.text("Temp ( F )",text_location_1, 320,3);
    graphics.text("Humid ( % )", text_location_2, 320,3);
    graphics.text("Press ( inHg )", text_location_3, 320,3);
    // Define location, order, and color of display
    graphics.text(Temp, text_location_5, 320,3); // temp
    graphics.text(Humid, text_location_6, 320,3); // humid
    graphics.text(Press, text_location_7, 320,3); // press
    graphics.set_pen(RED);
    
    // update screen
    st7789.update(&graphics); // update screen
    sleep_ms(1000); // update each second */

So, getting out of the loop is the big problem right now. Still searching for other methods that may work better.

You will get out of your loop if you either have fixed sized messages (i.e. you have to count bytes) or if you have a message terminator. The terminator can be anything (single or multiple char), e.g. CRLF, the only requirement is that it is not part of your message.

I’ve made some progress. By having a if() statement, I can break out of the loop. But that is a not so good kludge, and many times I get partial messages. There has to be a simpler way, but I am not that familiar with C++ to know what works reliably to determine the message length, as there is some variation for each. I am using the following to at least get some data to the display, on the receive end, to prove it can be done.

   ...
    vector<uint8_t> buffer;
    int i=0;
    // Read and store in a buffer.
    while((c=uart_rx_program_getc(pio, sm)) !=EOF) { // Works, never exits
        buffer.push_back(c);
        putchar(c);
        i++;
        if(i>=46) { break; }
    }
   ...

So, the pico display 2.8 can process the data and display it. For reference, this is the code to send to the second pico housing the display.

    string message = "T: " + Temp + "\nH: " + Humid + "\nP: " + Press + "\n" ; // assemble complete message
    
    char* c = &*message.begin();

    uart_puts(UART_ID, c); // send message out UART TX (GP0) each 'receive()' cycle
        sleep_ms(5000);
  }

Any suggestions are welcome.
What the above is doing is receiving the message, doing nothing; then, when the next message is received, and the buffer exceeds 46 characters, it breaks out and processes the first message. So, it is processing about every second message.

I can only repeat my suggestion of my last post. If your messages are of variable length, then add a message terminator and instead of the useless checking of EOF just check for the terminator. You can use any character as a terminator that you don’t use for your message. As far as I can see, you are using numbers, decimal point, colon; space, “T”, “H”, “P” and “\n” in your message. So just use any character in the alphabet that is not in this list and you are fine.

@bablokb, thank you for insight into the fix. Your second repeated response was clearer than the first. I was attempting to discern what you meant when you said, “…the only requirement is that it is not part of your message.” I was scratching my head trying to figure how to send something that was not part of the message. So, your second reply led me to understand you meant not embedded as part of the important parts of the message.
So, now I have this on the final receiver which does the processing for the pico display 2.8.

while((c=uart_rx_program_getc(pio, sm)) != 'Z') {
  buffer.push_back(c);
  putchar(c);
  i++;
}

it also works with:

if(c == 'Z') { break; } // this causes exit

inside the while() loop, but is more cumbersome. On the transmitter end, I changed the string sent to the following, using ‘Z’ as the ending character.

string message = "T: " + Temp + "\nH: " + Humid + "\nP: " + Press + "\nZ" ;

Now I am processing each message correctly, every time. Thanks for all the help!