I'm trying to demodulate a GFSK signal coming from an nRF24L01+ transceiver chip (hooked up to my Arduino). I've followed this guide so far:
https://www.bitcraze.io/2015/06/sniffing-crazyflies-radio-with-hackrf-blue/#comment-38046
..and managed to manually demodulate a package (the address and the message I sent 'martijn' are clearly recoverable):
https://drive.google.com/open?id=0B9CJ42CGPiF2TWoyelRmWldZcU0
However, now I want to receive packets and decode them as they come in. Someone already made a decoder for this job, but somehow it fails to find my nRF24 packets:
https://wiki.bitcraze.io/misc:hacks:hackrf
My Arduino code for sending the packets is as followed:
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
RF24 radio(9,10);
const uint64_t pipe = 0xe7e7e7e7e7;
char package[] = "martijn";
void setup() {
Serial.begin(9600);
radio.begin();
radio.setDataRate(RF24_1MBPS);
radio.setChannel(95);
radio.openWritingPipe(pipe);
radio.enableDynamicPayloads();
radio.setAutoAck(true);
radio.powerUp();
}
void loop() {
radio.write(&package, strlen(package));
delay(1);
}
Basically I just want to use GNU Radio Companion to obtain the nRF24 packets, and send their binary data into a file. I'm fine with writing my own decoder. However, I have no clue on how to get this binary data from the incoming signals.
(The comments at the bitcraze site are also mine)
I've be very happy if someone could help me (or even point me in the right direction). Thanks in advance!
After the Quadrature Demod you have to use a Clock recovery block. The M&M Clock Recovery of GNU Radio should do the job. This block will dramatically increase the performance of the decoding.
However you have to take care some parameters that this block requires. The most important is the 'omega'. 'Omega' roughly speaking corresponds to the number of samples per symbol. For example, if your GFSK baudrate is 9600 and your incoming signal from the hardware is 96000, each symbol corresponds to 10 samples. The omega can be any float number. Note however, that clock recovery does not work for large omega values. So try to keep the omega up to 8.0. To do that, either adjust properly the hardware sampling rate or do some resampling.
After the Clock Recover just use a 'Binary Slicer' block. This will convert the floats to bits of 0's and 1's. Using the Pack K bits block you can convert the bit stream into byte stream, that can easily saved to file with a 'File Sink'.
Here is a good step-by-step tutorial for an FSK receiver. GFSK adds only a Gaussian filter so the procedure is quite the same for both of them.
Related
I have been experimenting with message passing in the Signal Source block in GNU Radio companion. I can see from its source code that we can pass messages to change the frequency, amplitude, offset and phase of the source. For example, the following message PMT sent from a message strobe can change the amplitude of the signal to 0.5.
pmt.dict_add(pmt.make_dict(), pmt.intern("ampl"), pmt.from_double(0.5))
But when I viewed the code of UHD USRP Sink, I couldn't get a clear idea as to what commands can be sent to this block or that which parameters can be changed. I have read at some places in the documentation that frequency, gain, LO offset, timestamp, center frequency and other transceiver related settings of the USRP Sink can be manipulated through command messages.
What commands can be sent to the USRP Sink block from a message strobe (in the pmt format) and which parameters (and their keys) can be modified?
This is officially documented:
https://www.gnuradio.org/doc/doxygen/page_uhd.html#uhd_command_syntax
Command name
Value Type
Description
chan
int
Specifies a channel. If this is not given, either all channels are chosen, or channel 0, depending on the action. A value of -1 forces 'all channels', where possible.
gain
double
Sets the Tx or Rx gain (in dB). Defaults to all channels.
power_dbm
double
Sets the Tx or Rx power reference level (in dBm). Defaults to all channels. Works for certain devices only, and only if calibration data is available.
freq
double
Sets the Tx or Rx frequency. Defaults to all channels. If specified without lo_offset, it will set the LO offset to zero.
lo_offset
double
Sets an LO offset. Defaults to all channels. Note this does not affect the effective center frequency.
tune
tune_request
Like freq, but sets a full tune request (i.e. center frequency and DSP offset). Defaults to all channels.
mtune
tune_request_t
Like tune, but supports a full manual tune request as uhd::tune_request_t. Defaults to all channels.
lo_freq
double
For fully manual tuning: Set the LO frequency (RF frequency). Conflicts with freq, lo_offset, and tune.
dsp_freq
double
For fully manual tuning: Set the DSP frequency (CORDIC frequency). Conflicts with freq, lo_offset, and tune.
direction
string
Used for timed transceiver tuning to ensure tuning order is maintained. Values other than 'TX' or 'RX' will be ignored.
rate
double
See usrp_block::set_samp_rate(). Always affects all channels.
bandwidth
double
See usrp_block::set_bandwidth(). Defaults to all channels.
time
timestamp
Sets a command time. See usrp_block::set_command_time(). A value of PMT_NIL will clear the command time.
mboard
int
Specify mboard index, where applicable.
antenna
string
See usrp_block::set_antenna(). Defaults to all channels.
gpio
gpio
PMT dictionary including bank, attr, value, mask for GPIO. See notes.
I'm just trying to learn to use external ADC and DAC (PT8211) with my PIC32MX534f06h.
So far, my code is just about sampling a signal with my ADC every time a timer-interrupt is triggered, then sending then same signal out to the DAC.
The interrupt and ADC part works fine and have been tested independently, but the voltages that my DAC outputs don't make much sens to me and stay at 2,5V (it's powered at 0 - 5V).
I've tried to feed the DAC various values ranging from 0 to 65534 (16bits DAC so i guess it should be the expected range of the values to feed to it, right?) voltage stays at 2.5V.
I've tried changing the SPI configuration, using different SPIs (3 and 4) and DACs (I have one soldered to my pcb, soldered to SPI3, and one one breadboard, linked to SPI4 in case the one soldered on my board was defective).
I made sure that the chip selection line works as expected.
I couldn't see the data and clock that are transmissed since i don't have a scope yet.
I'm a bit out of ideas now.
Chip selection and SPI configuration settings
signed short adc_value;
signed short DAC_output_value;
int Empty_SPI3_buffer;
#define Chip_Select_DAC_Set() {LATDSET=_LATE_LATE0_MASK;}
#define Chip_Select_DAC_Clr() {LATDCLR=_LATE_LATE0_MASK;}
#define SPI4_CONF 0b1000010100100000 // SPI on, 16-bit master,CKE=1,CKP=0
#define SPI4_BAUD 100 // clock divider
DAC output function
//output to external DAC
void DAC_Output(signed int valueDAC) {
INTDisableInterrupts();
Chip_Select_DAC_Clr();
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write byte to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF; // read RX buffer
Chip_Select_DAC_Set();
INTEnableInterrupts();
}
ISR sampling the data, triggered by Timer1. This works fine.
ADC_input inputs the data in the global variable adc_value (12 bits, signed)
//ISR to sample data
void __ISR( _TIMER_1_VECTOR, IPL7SRS) Test_data_sampling_in( void)
{
IFS0bits.T1IF = 0;
ADC_Input();
//rescale the signed 12 bit audio values to unsigned 16 bits wide values
DAC_output_value = adc_value + 2048; //first unsign the signed 12 bit values (between 0 - 4096, center 2048)
DAC_output_value = DAC_output_value *16; // the scale between 12 and 16 bits is actually 16=65536/4096
DAC_Output(DAC_output_value);
}
main function with SPI, IO, Timer configuration
void main() {
SPI4CON = SPI4_CONF;
SPI4BRG = SPI4_BAUD;
TRISE = 0b00100000;
TRISD = 0b000000110100;
TRISG = 0b0010000000;
LATD = 0x0;
SYSTEMConfigPerformance(80000000L); //
INTCONSET = _INTCON_MVEC_MASK; /* Set the interrupt controller for multi-vector mode */
//
T1CONbits.TON = 0; /* turn off Timer 1 */
T1CONbits.TCKPS = 0b11; /* pre-scale = 1:1 (T1CLKIN = 80MHz (?) ) */
PR1 = 1816; /* T1 period ~ ? */
TMR1 = 0; /* clear Timer 1 counter */
//
IPC1bits.T1IP = 7; /* Set Timer 1 interrupt priority to 7 */
IFS0bits.T1IF = 0; /* Reset the Timer 1 interrupt flag */
IEC0bits.T1IE = 1; /* Enable interrupts from Timer 1 */
T1CONbits.TON = 1; /* Enable Timer 1 peripheral */
INTEnableInterrupts();
while (1){
}
}
I would expect to see the voltage at the ouput of my DAC to mimic those I put at the input of my ADC, instead the DAC output value is always constant, no matter what I input to the ADC
What am i missing?
Also, when turning the SPIs on, should I still manually manage the IO configuration of the SDI SDO SCK pins using TRIS or is it automatically taken care of?
First of all I agree that the documentation I first found for PT8211 is rather poor. I found extended documentation here. Your DAC (PT8211) is actually an I2S device, not SPI. WS is not chip select, it is word select (left/right channel). In I2S, If you are setting WS to 0, that means the left channel. However it looks like in the extended datasheet I found that WS 0 is actually right channel (go figure).
The PIC you've chosen doesn't seem to have any I2S hardware so you might have to bit bash it. There is a lot of info on I2S though ,see I2S bus specification .
There are some slight differences with SPI and I2C. Notice that the first bit is when WS transitions from high to low is the LSB of the right channel. and when WS transitions from low to high, it is not the LSB of the left channel. Note that the output should be between 0.4v to 2.4v (I2S standard), not between 0 and 5V. (Max is 2.5V which is what you've been seeing).
I2S
Basically, I'd try it with the proper protocol first with a bit bashing algorithm with continuous flip flopping between a left/right channel.
First of all, thanks a lot for your comment. It helps a lot to know that i'm not looking at a SPI transmission and that explains why it's not working.
A few reflexions about it
I googled Bit bashing (banging?) and it seems to be CPU intensive, which I would definately try to avoid
I have seen a (successful) projet (in MikroC) where someone transmit data from that exact same PIC, to the same DAC, using SPI, with apparently no problems whatsoever So i guess it SHOULD work, somehow?
Maybe he's transforming the data so that it works? here is the code he's using, I'm not sure what happens with the F15 bit toggle, I was thinking that it was done to manage the LSB shift problem. Here is the piece of (working) MikroC code that i'm talking about
valueDAC = valueDAC + 32768;
valueDAC.F15 =~ valueDAC.F15;
Chip_Select_DAC = 0;
SPI3_Write(valueDAC);
Chip_Select_DAC = 1;
From my understanding, the two biggest differences between SPI and I2S is that SPI sends "bursts" of data where I2S continuously sends data. Another difference is that data sent after the word change state is the LSB of the last word.
So i was thinking that my SPI is triggered by a timer, which is always the same, so even if the data is not sent continuously, it will just make the sound wave a bit more 'aliased' and if it's triggered regularly enough (say at 44Mhz), it should not be SO different from sending I2S data at the same frequency, right?
If that is so, and I undertand correctly, the "only" problem left is to manage the LSB-next-word-MSB place problem, but i thought that the LSB is virtually negligible over 16bit values, so if I could just bitshift my value to the right and then just fix the LSB value to 0 or 1, the error would be small, and the format would be right.
Does it sounds like I have a valid 'Mc-Gyver-I2S-from-my-SPI' or am I forgetting something important?
I have tried to implement it, so far without success, but I need to check my SPI configuration since i'm not sure that it's configured correctly
Here is the code so far
SPI config
#define Chip_Select_DAC_Set() {LATDSET=_LATE_LATE0_MASK;}
#define Chip_Select_DAC_Clr() {LATDCLR=_LATE_LATE0_MASK;}
#define SPI4_CONF 0b1000010100100000
#define SPI4_BAUD 20
DAaC output function
//output audio to external DAC
void DAC_Output(signed int valueDAC) {
INTDisableInterrupts();
valueDAC = valueDAC >> 1; // put the MSB of ValueDAC 1 bit to the right (becase the MSB of what is transmitted will be seen by the DAC as the LSB of the last value, after a word select change)
//Left channel
Chip_Select_DAC_Set(); // Select left channel
SPI4BUF=valueDAC;
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write 16-bits word to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF; // read RX buffer (don't know why we need to do this here, but we do)
//SPI3_Write(valueDAC); MikroC option
// Right channel
Chip_Select_DAC_Clr();
SPI4BUF=valueDAC;
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write 16-bits word to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF;
INTEnableInterrupts();
}
The data I send here is signed, 16 bits range, I think you said that it's allright with this DAC, right?
Or maybe i could use framed SPI? the clock seems to be continous in this mode, but I would still have the LSB MSB shifting problem to solve.
I'm a bit lost here, so any help would be cool
I want to get the temperature from a thermistor, so I made a voltage divider (3.3V to 10k resistor and between ground a 10k thermistor) I read the ADC between the 10k resistor and the thermistor.
The BCOEFFICIENT is 3977, the NOMINAL TEMPERATURE is 25C and I use the simple B parameter equation. I'm not sure where I'm doing mistake, I read room temperature as 10.5C which was suppose to be around 24C. The following is the part of the program that I used for temperature sensor(developed in AVR studio),
#define TEMPERATURENOMINAL 25
#define TERMISTORNOMINAL 10000
#define BCOEFFICIENT 3977
#define SERIESRESISTOR 10000
{
float ke1,tempa,xin
ke1 = adc_get_value(peak_adc2,peak2);
xin=(1023/ke1)-1;
xin=SERIESRESISTOR/xin;
tempa=xin/TERMISTORNOMINAL;
tempa=log(tempa);
tempa/= BCOEFFICIENT;
tempa+=1.0/(TEMPERATURENOMINAL + 273.15);
tempa=1.0/tempa;
tempa-=273.15;
dip204_set_cursor_position(1,3);
//sprintf(ui, "Temp is %.2f deg", Ref);
sprintf(ui, "Temp is %.2f deg", tempa);
dip204_write_string(ui);
}
I checked the voltage using multi-meter for instance in between the thermistor and 10k resistor and in the EVK 1100 using the following line
ke1 = adc_get_value(peak_adc2,peak2)*3.3/1024;
I get the same voltage in both.
Not sure where I'm doing a mistake, Hope someone guide me in right direction
Your code looks correct to me, and I suspect a hardware problem may be the culprit.
It seems likely you have inadvertently connected two 10K-ohm pull-up resistors between the ADC input and the +3.3V reference: perhaps one is already populated on the EVK1100 board, and you have added another one externally connected to your thermistor. This would be equivalent to putting both 10K-ohm resistors in parallel with each other, which would be equivalent to a 5K-ohm resistor in series with the thermistor. At 25°C, the thermistor resistance Rt would read 10K ohms, which would produce a voltage of:
+3.3V * (Rt / (Rt + 5K))
= 2.20V
instead of the correct +1.65V. This number is very close to the result you are seeing (+2.17V # 24°C).
You can verify this hypothesis by looking at the schematic and/or PCB for the EVK1100 to see if a 10K-ohm pull-up resistor is connected from the ADC input to +3.3V. If this is the problem, remove one of the two resistors and you should see correct behavior.
I have the following code snippet:
#include "contiki.h"
#include <stdio.h> /* For printf() */
PROCESS(calc_process, "calc process");
AUTOSTART_PROCESSES(&calc_process);
PROCESS_THREAD(calc_process, ev, data)
{
double dec=13.2, res=0, div=3.2;
PROCESS_BEGIN();
res=dec+div;
printf("%f",res);
PROCESS_END();
}
After uploading the above code in Tmote sky platform using the command
make TARGET=sky calc.upload, the program will be loaded to the mote (there is no error). Then login to the mote using make login TARGET=sky, the following output is displayed....
OUPUT:
**Rime started with address 4.0
MAC 04:00:00:00:00:00:00:00 Contiki 2.7 started. Node id is set to 4.
CSMA ContikiMAC, channel check rate 8 Hz, radio channel 26
Starting 'calc process'
%f**
How can I get the correct value?
Thanks
It is not floating point calculation support that you need - you have that already. What is missing is floating point support within printf(). That is to say that res will be calculated correctly, but printf() does not support its display.
Because it requires a relatively large amount of code, many microcontroller targeted libraries omit floating point support in stdio. There may be a library build option to include floating point support - refer to the library documentation.
You might do well to ask a question about the specific calculation necessary, and how it might be done using integer or fixed point arithmetic. Alternatively you might write your own floating point display as described here: How to print floating point value using putchar? for example.
I know that there is a lot in internet (http://forum.arduino.cc/index.php?topic=159557.0 for example) about OV7670 and I read a lot about it, but seems something is missing.
First of all I took a look into the way how can we read pixel by pixel from the camera to build the rectangular 600 X 480 image, and this was quite easy to understand considering HREF, VSYNCH and PCLOCK described on documentation here: http://www.voti.nl/docs/OV7670.pdf. I understand XCLOCK as an input I need to give to OV7670 as a kind of cycle controller and RESET would be something to reset it.
So at this point I thought that the functionality of such camera would be covered by wiring the following pins:
D0..D7 - for data (pixel) connected to arduino digital pins 0 to 7 as INPUT on arduino board
XCLK - for camera clock connected to arduino digital pin 8 as OUTPUT from arduino board
PCLK - for pixel clock connected to arduino digital pin 9 as INPUT on arduino board
HREF - to define when a line starts / ends connected to arduino digital pin 10 as INPUT on arduino board
VSYCH - to define when a frame starts / ends connected to arduino digital pin 11 as INPUT on arduino board
GRD - groud connected to arduino GRD
3V3 - 3,3 INPUT connected to arduino 3,3v
RESET - connected to arduino RESET
PWDN - connected to arduino GRD
The implementation for such approach from my point of view would be something like:
Code:
for each loop function do
write high to XCLK
if VSYNCH is HIGH
return;
if HREF is LOW
return;
if lastPCLOCK was HIGH and currentPCLOCK is LOW
readPixelFromDataPins();
end for
My readPixelFromDataPins() basically read just the first byte (as I'm just testing if I can even read something from the camera), and it is written as follows:
Code:
byte readPixelFromDataPins() {
byte result = 0;
for (int i = 0; i < 8; i++) {
result = result << 1 | digitalRead(data_p[i]);
}
return result;
}
In order to check if something is being read from the camera I just print it to the Serial 9600, the byte read from data pins as a number. But currently I'm receiving only zero values. The code I'm using to retrieve an image is stored here: https://gist.github.com/franciscospaeth/8503747.
Did somebody that makes OV7670 work with Arduino already figure out what am I doing wrong? I suppose I'm using the XCLOCK wrongly right? What shall I do to get it working?
I searched a lot and I didn't found any SSCCE (http://sscce.org/) for this camera using arduino, if somebody have it please let me know.
This question is present on arduino forum (http://forum.arduino.cc/index.php?topic=211741.0) too.
your idea is not bad but ...
the xclock need to be a clock (in your program is just a transition from 0 to 1 and is freezing there)
you need also to use I2C with SIOC and SIOD for configuring the camera (or you can use the default settings, but I am not sure if is the correct output format for you, 30F/s,VGA, YUV format ....)
your code execution is slower using the serial output in the same loop with reading data
I will recommend you to toggle the xclock pin and to move the pixel print in a if(). Also you will be able to read Data only in a very precise time, if you want to read only one byte, than after a transition from 0 to 1 of HREF you need to wait for a new transition from 0 to 1 of PCLK (you will be able to see only one 0-1 transition of HREF after 784x2 transitions of PCLK, (640 active pixels + 144 dead time for each line) x 2 (for YUV or RGB are 2 bytes received for each pixel) )
Hello I am Mr_Arduino from the arduino forums. Your issue is that you are reading pixels too slow please do not use digital read to do such a thing. Also if you insist on using a separate function just to read a byte make sure the function is being inlined. You can do this by declaring your function as static inline. Also as mentioned above how are you generating the clock. You can generate the XCLK using PWM on the arduino.
I have created a working example here:
https://github.com/ComputerNerd/arduino-camera-tft/blob/master/captureimage.c
Edit: a 3rd party has copied part but not all of the code from the above link into the answer here. However, the link must remain as the code posted below requires additional files from that source to actually work.
Edit 2: Removed irrelevant code. You will need to modify what you do with the data.
void capImg(void){
cli();
uint8_t w,ww;
uint8_t h;
w=160;
h=240;
tft_setXY(0,0);
CS_LOW;
RS_HIGH;
RD_HIGH;
DDRA=0xFF;
//DDRC=0;
#ifdef MT9D111
while (PINE&32){}//wait for low
while (!(PINE&32)){}//wait for high
#else
while (!(PINE&32)){}//wait for high
while (PINE&32){}//wait for low
#endif
while (h--){
ww=w;
while (ww--){
WR_LOW;
while (PINE&16){}//wait for low
PORTA=PINC;
WR_HIGH;
while (!(PINE&16)){}//wait for high
WR_LOW;
while (PINE&16){}//wait for low
PORTA=PINC;
WR_HIGH;
while (!(PINE&16)){}//wait for high
WR_LOW;
while (PINE&16){}//wait for low
PORTA=PINC;
WR_HIGH;
while (!(PINE&16)){}//wait for high
WR_LOW;
while (PINE&16){}//wait for low
PORTA=PINC;
WR_HIGH;
while (!(PINE&16)){}//wait for high
}
}
CS_HIGH;
sei();
}
You can also find it on github.
You can use my instruction: how to retrieve image from ov7670 It contains all the steps you need. There is also instuction to setup FrameGrabber: how to run framegrabber