Hi all, I'm considering using an rfm69 with a raspberry pi to send an image to another raspberry pi.
Is this possible? Basically I need to be able to send the Raspberry Pi with a camera a command and then it will send an image back to the raspberry pi that I am controlling, kind of like a remote digital camera.
Would I need to encode the image in any way to be able to send it via an RFM69?
Sorry if this is a vague scenario, but I don't have any components yet and I haven't touched a developer board in around 3 years so I'm a little rusty.
All I need to do is control a raspberry pi ~250-350m away and have it send single small images back. Cellular data is not an option due to location and the 900 Mhz range is ideal for range and propagation.
A couple links that may be of interest.
A DIY low-cost LoRa gateway - http://cpham.perso.univ-pau.fr/LORA/RPIgateway.html
A low-power, low-cost image sensor board - http://cpham.perso.univ-pau.fr/WSN-MODEL/tool-html/imagesensor.html
Looking at the image sensor board but unfortunately the C++ encoding program that they have posted is broken, it will generate a dat file for the image but immediately after that it ends with a segmentation fault (core dumped). I'm currently going through the code to see if any silly mistakes stand out. It's been a LONG time since I've even look at C :-[.
Also if anyone cares, the project I have in mind is to make a Sojourner style rover. The plan is to have the rover execute a series of commands, stop, send a few pictures, and wait for another command.
I've done some tests and it's possible. But it's slow, low bandwidth. And with high datarates of even 300kbps (max with RFM69/sx1231 FSK modulation) you can't plan to send MBs of data, it could take too long and OTA could break down. Maybe using lossy image streams where you could loose some data without corrupting the entire image, but that type of protocol gets even more complex to code and handle.
Then the longer the link is the slower the speed needs to be to make it realible. So overall, this would be suitable for OTA reflashing, but not really for streaming media.
You'd be better off with ESP/wifi but then range sucks.
Perhaps 3G/4G cellular links?
That's the bulk of what I've seen while searching, but it's usually people that want to send pictures that are MBs of data. Taking a 320x240 picture with a webcam yields a picture that is 14.4 KBs. Considering that this would be used solely for navigation, quality isn't really that important, so the image could be compressed or reduced to be even smaller. Although, even at 14.4 KBs thats still roughly 250 transmissions per image.
When you say streaming media are you referring to a stream of images or the stream of data to make 1 image? I will not be trying to send more than one image at a time and there will be no requirement for real time imaging, so slow data transfers are fine with me.
As far as other wireless options, wifi is out due to the 2.4/5.8 Ghz that it operates on, far too little range and is destroyed by most obstructions. I've thought about 3G, but coverage is spotty at best and both of these options don't feel as much like a Mars rover to me as an RF link. An added plus with RF would be that I could use a repeater if I needed.
Also looking around at image encoding options, any thoughts on base64 encoding?
Quote from: MonkeyMan on January 27, 2018, 10:06:19 PM
When you say streaming media are you referring to a stream of images or the stream of data to make 1 image?
Streaming of 1 file, whatever the content.
Quote from: MonkeyMan on January 27, 2018, 10:06:19 PM
Also looking around at image encoding options, any thoughts on base64 encoding?
Encode the image bitstream? They are already encoded, why bother doing it again?
If you can deal with base64, I think you could deal with something like JPEG encoding.
There are some folks (see Dave Akerman (http://www.daveakerman.com/)'s blog) that send images from space (high altitude balloons). He's using LoRa to stream images from a Pi in the sky, low bitrate, but it works. Maybe this would work for you as well?
Quote from: Felix on January 29, 2018, 08:32:19 AM
Encode the image bitstream? They are already encoded, why bother doing it again?
If you can deal with base64, I think you could deal with something like JPEG encoding.
I was talking about encoding the image itself into a base64 text file to be able to easily chop the text file into chunks to be transmitted. I was able to convert an image to a text file.
Don't overestimate my knowledge on this particular subject :P I would consider myself a moderately knowledgeable user of Linux and a novice to Arduino and programming. There is a good chance that my understanding of the library/module is wrong or incomplete. I think I'm honestly just a little confused at this point as to what this chip can and cannot do.
QuoteThere are some folks (see Dave Akerman's blog) that send images from space (high altitude balloons). He's using LoRa to stream images from a Pi in the sky, low bitrate, but it works. Maybe this would work for you as well?
Yes, this is basically what I'm trying to achieve. The only two problems I'm having are that I'd like to stick with the rfm69 and it also includes a lot that I don't really have a use for and with all of the headers and main C files weaved together it makes it much harder for me to just pick and choose pieces that I like out the code to use.
At this point I've ordered a few RFM69s and I'll toy around with them and get familiar with them first and come back with some actual code and some actual attempts at solving the problem.
Thanks for all the help so far
Quote from: MonkeyMan on January 29, 2018, 09:31:44 PM
I was talking about encoding the image itself into a base64 text file to be able to easily chop the text file into chunks to be transmitted. I was able to convert an image to a text file.
All files are made of bytes, either the human readable alphanumeric spectrum of ASCII or
binary (still just 256 possible characters, but they look like a mess and not all chars are alphanumeric), see ASCII table here (http://www.asciitable.com/). The readable part is most of the characters up to 127 (the alphanumeric, and other important characters like TAB, SPACE, and CARRIAGE RETURN):
(http://www.asciitable.com/index/asciifull.gif)
Converting to base64 simply takes those bytes and make them look a little "more interesting" (well, base 64 instead of base 10). You can look at a binary file (like an image) through a hex viewer, you can see the same bytes in hex, they are more "readable" if you are used to looking at hex.
By the way
binary is actually base2 (0s and 1s) but it's common slang for files that contain data in some format that a program can understand (rather than human readable/text data).
Quote from: MonkeyMan on January 29, 2018, 09:31:44 PM
I'd like to stick with the rfm69
See if Akerman did any RFM69 trials before going to LoRa (I believe not from what I saw on his blog but try going back in time...). Or maybe others did.
If I ever get around to it, I may release a library which I developed that allows streaming long files (or any byte streams) through the RFM69, although in terms of legality this is questionable since it does not use spread spectrum and it can take a "long" time to stream a file of say 100K (say 15seconds, still long for the ISM RF spectrum). But this was very experimental and it will take lots more time to make it nice and clean to use, so no promises.
RFM69 itself does support even unlimited stream of data, id that is used in places of your own home at min possible power, that transmitting time is not important to you and now bother anybody else
Quote from: LukaQ on January 30, 2018, 12:07:14 PM
RFM69 itself does support even unlimited stream of data, id that is used in places of your own home at min possible power, that transmitting time is not important to you and now bother anybody else
Theoretically, it's called continuous mode.
You need some kind of protocol though, to piece together a stream of bytes, in a certain order. When you want to send a 100k binary file, the bytes need to come in order, not be corrupted or changed (ie data integrity). Some file types cannot afford corrupted bytes. You can probably do that with continuous mode, but then you're finding yourself having to implement a "packet" protocol to put your files back together, so may as well just use the native packet mode of the RF module. That's my take at least.
Quote from: Felix on January 30, 2018, 08:52:27 AM
If I ever get around to it, I may release a library which I developed that allows streaming long files (or any byte streams) through the RFM69, although in terms of legality this is questionable since it does not use spread spectrum and it can take a "long" time to stream a file of say 100K (say 15seconds, still long for the ISM RF spectrum). But this was very experimental and it will take lots more time to make it nice and clean to use, so no promises.
Regarding legality, in the US and I believe a few others, there is no duration or duty cycle limit on the 915 ISM band, only power limit. The only difference with spread spectrum is that more power is allowed. IMO I'm all for anyone writing any code they want, if someone misuses code to break regulations, the blame falls on the user not the person who wrote the code.
I did find something interesting, I found someone using a simple arduino program to emulate a half duplex serial PPP over loras, I'm not sure if that would be stable enough to send an image, but he claims that its enough to use SSH, so SCP may be close enough to be workable, it may also be simple enough that I could modify it to use with an RFM69 instead.
Heres the link : https://ds0.me/lora/index.html (https://ds0.me/lora/index.html)
Quote from: MonkeyMan on February 04, 2018, 06:51:42 PM
I found someone using a simple arduino program to emulate a half duplex serial PPP over loras, I'm not sure if that would be stable enough to send an image
My guess it's not, or would be painfully slow. LoRa is much lower bitrate than FSK.
What is your longest link distance?
Quote from: Felix on February 05, 2018, 04:49:18 PM
My guess it's not, or would be painfully slow. LoRa is much lower bitrate than FSK.
Whatever you use you'll need a error detection and re-transmission packet based protocol. Maybe you could hijack some of the old file transfer protocols which were designed for use with unreliable links like old 'tone' modems over poor quality telephone lines, such as Kermit (or a modified variant).
Mark.
Alright so I haven't given up on this completely, I have a working example but itself is made out of example code so it's more than likely inefficient, buggy, and unreliable, but I feel like its at least a start and it is cool to me to be able to send any file over an rf link.
What I've got is a python script that takes a file like a jpeg, converts it to a base64 text file (makes it easier to hand off to arduino), then splits it into 55 byte chunks and hands it to the arduino to send it.
On the receive side it does basically the exact opposite.
What needs work:
Obviously this is an extremely simple example, so if a packet comes incomplete or goes missing I'm pretty much screwed, I'm not sure what happens if the node sends a packet before the gateway is done writing the preceding packet, thats why I added the hefty delay between transmits. I also used the radiohead library because for some reason I couldn't get Felix's library working with my breakout boards, probably some error in my wiring that I couldn't figure out.
Here's the arduino node side
// rf95_reliable_datagram_client.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple addressed, reliable messaging client
// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio.
// It is designed to work with the other example rf95_reliable_datagram_server
// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with the RFM95W
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>
#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2
#define RF95_FREQ 915.0
RH_RF95 driver(4, 3);
char a = '0';
void setup()
{
// Rocket Scream Mini Ultra Pro with the RFM95W only:
// Ensure serial flash is not interfering with radio communication on SPI bus
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
Serial.begin(9600);
Serial.println(" ");
while (!Serial) ; // Wait for serial port to be available
if (!manager.init())
Serial.println("init failed");
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 5 to 23 dBm:
driver.setTxPower(10, false);
// If you are using Modtronix inAir4 or inAir9,or any other module which uses the
// transmitter RFO pins and not the PA_BOOST pins
// then you can configure the power transmitter power for -1 to 14 dBm and with useRFO true.
// Failure to do that will result in extremely low transmit powers.
// driver.setTxPower(14, true);
// You can optionally require this module to wait until Channel Activity
// Detection shows no activity on the channel before transmitting by setting
// the CAD timeout to non-zero:
// driver.setCADTimeout(10000);
}
//char a = Serial.read ();
// Dont put this on the stack:
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
void loop()
{
while(Serial.available()){
char a = Serial.read ();
if (manager.sendtoWait((uint8_t*)a, sizeof(a), SERVER_ADDRESS))
{
uint8_t len = sizeof(buf);
uint8_t from;
}
delay(500);
}
}
The receive code for arduino
// rf95_reliable_datagram_server.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple addressed, reliable messaging server
// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio.
// It is designed to work with the other example rf95_reliable_datagram_client
// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with the RFM95W
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>
#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
//RH_RF95 driver;
RH_RF95 driver(4, 3); // Rocket Scream Mini Ultra Pro with the RFM95W
// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, SERVER_ADDRESS);
// Need this on Arduino Zero with SerialUSB port (eg RocketScream Mini Ultra Pro)
//#define Serial SerialUSB
void setup()
{
// Rocket Scream Mini Ultra Pro with the RFM95W only:
// Ensure serial flash is not interfering with radio communication on SPI bus
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
Serial.begin(9600);
Serial.println(" ");
while (!Serial) ; // Wait for serial port to be available
if (!manager.init())
Serial.println("init failed");
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 5 to 23 dBm:
driver.setTxPower(10, false);
// If you are using Modtronix inAir4 or inAir9,or any other module which uses the
// transmitter RFO pins and not the PA_BOOST pins
// then you can configure the power transmitter power for -1 to 14 dBm and with useRFO true.
// Failure to do that will result in extremely low transmit powers.
// driver.setTxPower(14, true);
// You can optionally require this module to wait until Channel Activity
// Detection shows no activity on the channel before transmitting by setting
// the CAD timeout to non-zero:
// driver.setCADTimeout(10000);
}
// Dont put this on the stack:
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
void loop()
{
if (manager.available())
{
// Wait for a message addressed to us from the client
uint8_t len = sizeof(buf);
uint8_t from;
if (manager.recvfromAck(buf, &len, &from))
{
Serial.write((char*)buf);
}
}
delay(5);
}
The Python send script
import serial
import time
import os
os.system('base64 < d.jpg > pic.txt') # Linux builtin to convert binary files to base64 txt
CHUNK_SIZE = 55 # Maximum chunk size that can be sent
arduinoSerialData = serial.Serial("/dev/ttyUSB1",115200) # alias Arduino transmitter serial
myData = arduinoSerialData.readline() # Read to clear serial buffer
print myData
image_file = 'pic.txt' # Image converted to b64 txt file
with open(image_file, 'rb') as infile:
while True:
chunk = infile.read(CHUNK_SIZE)
if not chunk: break
print "Writing data"
arduinoSerialData.write(chunk) # write data to Arduin0
arduinoSerialData.write('\r') # Write carriage return to tell arduino to send message
time.sleep(3)
print "Sent"
And python receive script
import serial
import time
import os
arduinoSerialData2 = serial.Serial("/dev/ttyUSB0",115200)
myData = arduinoSerialData2.readline()
print myData
myData = arduinoSerialData2.readline()
print myData
file = open('trans.txt', 'wb')
while True:
if(arduinoSerialData2.inWaiting()>0):
myData = arduinoSerialData2.readline()
print myData
file.write(myData)
file.close