PiicoDev Transceivers and Images

Hi,

I’m up to no good again… I’m not sure if this is a stackoverflow type question, but figured i’d post here first as you fine folk at core-electronics might see value in this one?? (maybe)

I am trying to send an image from one PiicoDev Transceiver to another. To reduce swivel chair operations i’m running both PiicoDev Transceivers off of a single Raspberry Pi4B, with the first transceiver having ID 0,0,0,0 and the second transceiver having ID 1,0,0,0. Transceiver 2 is plugged into transceiver 1 which is attached to the PiicoDev interface board that is sitting on my Pi.

WHY (am I trying to send an image)
Great question and you probably think i am mad, but reality is - cellular communications isn’t always there, nor wifi (for those use cases I just use UDP/Sockets), but imagine you’re out in the bush with your 4x4, there’s no network coverage and you want to send a picture to the 4x4 in front of you over RF… :slight_smile:

The Challenge
I’ve written some sender and receiver code, however i’m not a natural python programmer and so I don’t know how to solve my challenge. The ‘sending/transmitter’ code works I think, it sends packets of data with a START and END ok. On the receiving code, it detects the START and then starts reading the data packets and appending them to received_data , again I think this works ok. However once all the packets are received I am then trying to display the image but get the message:

TypeError: a bytes-like object is required, not 'str'

I tried using an .encode() command on the string but that throws up a different type of error.

Anyhow keen to see if anyone knows what adjustments I’d have to make to achieve my mission (sending an image over a PiicoDev 915Mhz Transceiver).

SENDER Code

# The purpose of this code is to send an image file across a PiicoDev Transceiver

# Include statements for the transceiver
from PiicoDev_Transceiver import PiicoDev_Transceiver
from PiicoDev_Unified import sleep_ms

#setup the PiicoDev sensors / output devices
transmitter = PiicoDev_Transceiver(id=[0,0,0,0])


# Open and read the JPEG image
image_path = "/home/pimaster/Desktop/PiicoDev/Test-Image.jpg"
with open(image_path, 'rb') as image_file:
    image_data = image_file.read()


# Define the packet size for splitting the image data
PACKET_SIZE = 1024


# Create a function to split data into packets and send them
def send_data(data):

    packet_count = (len(data) + PACKET_SIZE - 1) // PACKET_SIZE
    print("Packet count is " + str(packet_count))
    
    #transmitter.send(f"START{packet_count}".encode())
    transmitter.send(f"START{packet_count}")
    sleep_ms(500)
    
    #Transceiver - Send packet of image data
    for i in range(0, len(data), PACKET_SIZE):
        packet = data[i : i + PACKET_SIZE]
        transmitter.send(packet)
        print('Sending packet number ' + str(i))
        sleep_ms(500)

    #transmitter.send(b"END")
    transmitter.send(f"END")
    print('Sending END packet')


try:
    send_data(image_data)
    print("Image sent successfully!")

except Exception as e:
    print("Error sending image:", e)

RECEIVER Code

# This is my receiver code.  The intention is to receive packets of image data from a
# transceiver and then show the image once all the packets have been received.


# Include/imports statements for the transceiver
from PiicoDev_Transceiver import PiicoDev_Transceiver
from PiicoDev_Unified import sleep_ms

# Include/imports for image stuff
from PIL import Image
import io
import re

#setup the PiicoDev sensors / output devices
receiver = PiicoDev_Transceiver(id=[1,0,0,0])

# Define our global vars
received_data = ""
expected_packet_count = 0
current_packet_count = 0
        

# Listen for incoming connections and data
def lookforstart():
    startnotfound = True
    global expected_packet_count
    
    while True:
        if receiver.receive():
            message = receiver.message
            
            if ('START' in message):
                numberofpackets = int(re.search(r'\d+', message).group())
                expected_packet_count = numberofpackets
                print("Expected packet count is " + str(expected_packet_count))
                startnotfound = False
                if startnotfound == False:
                    break
            

def getimagedata():
    global expected_packet_count
    global current_packet_count
    global received_data
      
    while True:
        
        if receiver.receive():
            
            data = receiver.message

            print("Received packet " + str(current_packet_count) + " of " + str(expected_packet_count))
            
            if ("START" in data):
                print("** Have found start again so that might be a new image")
                expected_packet_count = int(data[5:])
                print(f"Expecting {expected_packet_count} packets.")
                current_packet_count = 0
                received_data = b""
                
            #elif data.startswith(b"END"):
            elif ("END" in data):
                print("** Have come to the end of the data stream")
                
                # received_data += data
                if current_packet_count == int(expected_packet_count):
                    
                    # I did try to encode the string but that generates a different type of error
                    #encoded_data = received_data.encode()
                    #image = Image.open(io.BytesIO(encoded_data))
                    
                    image = Image.open(io.BytesIO(received_data))
                    image.show()
                    print("Image received successfully!")
                else:
                    print("Received packet count doesn't match the expected count.")
                    expected_packet_count = 0
                    current_packet_count = 0
                    received_data = b""
                    break
            
            current_packet_count += 1
            received_data += data


try:
    # Look for the initial START packet and get the intended packet count
    lookforstart()
    
    # Now get the data packets and display the image
    getimagedata()
    
    
except KeyboardInterrupt:
    print("Terminating...")

Last thing to mention is you’ll see that I slowed the sending down by introducing a sleep of 500ms, this was intentional for the moment. Once the code works then the sleeps can be removed and I guess the transceiver speed needs to be set to 300kbps (maybe) but I am not sure what that will do to the range - again this is something I was looking to test… Would be good to be able to send a message over 50-100m maybe…

2 Likes

Python types can be confusing and Python often has to guess what type you want to use and gets it wrong.
Bytes and Str objects are handled differently by Python.
Images are stored as bytes objects.
So the TX code must send bytes and the RX code must put them into a bytes object.

Did some testing; the TX code has the right type, the RX does not.

received_data = "" Python thinks you want to declare a string and assigns str type.
received_data = b'' Use this and it assign a bytes object.
print(type(received_data) will show what type Python is using.

Probably detecting start and end will no longer work as these a str objects.
You will have to rethink how you are doing it as bytes objects.

All the best
Jim

4 Likes

Many Thanks for the tips Jim, i’m working through this, I have made some good progress - i’ll let you know when i’ve nailed it however I wanted to say Thank You for your guidance :), cheers James.

2 Likes

Not really an answer to your question but :slight_smile:
Years ago I had a similar problem when out 4wd ing my friends wanted to send pictures to each other and there was no nothing signal wise and one person pipes up and says just email it!
So I thought why not. Well not on the same trip anyway.
I built a raspberry pi with a wifi dongle because it wasn’t built in back then, configured it as an access point with no security. Installed an email server and configured some generic usernames like user1 and user2 password/password.
I Just plugged it into a usb power source and said have at it.
It worked great but I don’t think I ever got anyone to use it more than an interesting test :slight_smile:
It was a fun project. Eventually I raided the parts.
I think the email account is still configured on my phone.

3 Likes

Hey David,

That’s an awesome little system to use the early Pi with!

One project I saw a couple years ago was similar, a group of mates were going on a road trip and were using WIFI Routers as a way of trying to get communication set up between their cars. Just working through Laptops that were with the backseat passengers. It was thoroughly interesting to see an uncommon use for a device we use every day.

Cheers,
Blayden

1 Like

Hi James,

Once you have it working with PiicoDev I’d check out ESP-NOW. It works on ESP boards only (I recommend any of the unexpected maker boards with UFL to get range).

I found a video online showing that it can reach speeds faster than UART (Arduino) but there are micropython ports.

2 Likes

Thanks Liam for the tip… I will check out ESP NOW once I get the PiicoDev Transceiver doing what I need it to do, although I also have a couple of other longer range transceivers that I will also test too (once I get the PiicoDev image transfer working)… Exciting times. I think I am edging nearer :slight_smile:

1 Like

Many thanks for the feedback and idea David… definitely som out of the box thinking there… :slight_smile:

Hi James,

Sounds good! If you are using the send bytes command to relay information you’ll be able to drop ESP-NOW in as a replacement pretty much.

Keen to see how you go!

(PS: This would make for a great-writeup on the Core projects page, maybe if you combined it with the ArduCam MEGA cameras you could make an all in one picture capturing system: ArduCam Mega 5MP Camera | AC-B0401 | Core Electronics Australia)

1 Like

Actually - a question back to the Core Electronics team…

I can send data from a PiicoDev Transceiver using transmitter.send(b"{packet}") and it appears that the packet is being sent as a data stream… If I do a print(packet[:20]) command then I can see that my packets contain data.

However - how do I receive a message from the transmitter as a data stream as it seems that receiver.message is always being interpreted as a string (and not data).

Thoughts?

Cheers James

1 Like

Hi James,

If you’re keen on sending purely bytes objects I’d use the send_bytes command for both sending and receiving your image.

(And struct is a useful tool for packing and unpacking if you care to venture)

In the driver the radio.message is always being cast as a string.

1 Like

Hey Liam - I have it working !! Thank you for your tips on using send_bytes and receive_bytes. Shame I can’t use byte streams as then I could have a much larger packet stream, instead I have to limit it to 60bytes per packet. I will do a write up and paste the results here. Interestingly if you don’t introduce a small sleep between packet sends then the receiver end is unable to display the picture and you get a broken data stream error. However a 5ms delay between packet sends works, although it takes about 21.5 seconds to send a 640x480 image short range. I need to do some further testing but I am excited by the results.

Again Thank You so much :),

Best wishes, James.

1 Like

Ps. I can’t wait to do this with the HaLows, hopefully I’ll get onto that this week too :), am soooo excited…

1 Like

Hi James,

Awesome - if you have some videos I’m sure everyone in the thread would love to see them!
Curious that the delay really matters, might be something on the I2C end.

There are some settings inside of the transciever to get higher speeds but handling the swappover might be annoying.

Liam

1 Like