Showing posts with label RF. Show all posts
Showing posts with label RF. Show all posts

Monday, May 27, 2013

Raspberry pi: nRF24L01 and TCP

This time I will describe how I got a Raspberry pi (Wheezy) to work as a Python server that controls a RF-transmitter (nRF24L01). The server takes commands either from a TCP connection (internet) or via the keyboard, and broadcasts the data through the RF-transmitter to the receivers i have set up in my home automation system. See my other posts covering how to use the nRF with an AVR, and the construction of a  multi-functional remote control.

        

Why use a Raspberry pi when i already have a working TCP-server on my PC?

Easy answered:
  • I do not like to have my PC up and running 24-7 due to the facts that it's noisy (stationed in my bedroom)
  • The RPi takes much less energy (runs on a 1000mA cellphone charger)
  • I don't need any other parts other than a RPi, a nRF24L01(+) and cables, since the RPi has a 3,3V power supply!

Setting up SPI on RPi

First of all, you must have a working copy of raspbian, I use Wheezy (made a small tutorial in how to set it up). The RPi has a built in hardware SPI, which we first has to get up and running according to this guide, and here is how i did it:

Start a terminal like the LXTerminal and follow these commands:



To check if the SPI is working (optional) you can connect the mosi-pin to the miso-pin (GPIO10 and 9, see picture), and run the commands in a terminal:




Quick2Wire

Quick2Wire is a tool that makes it possible to control the GPIO and the SPI via python. We need to install quick2wire, download the quick2wire python plugin (API) and gain root access to the spi.
Start by downloading the plugin from there website. Extract the zip-file by right clicking and unzip. Now open a terminal window and change directory so that you are in the extracted folder called "quick2wire-gpio-admin-master" then enter the following commands:


Now log out from the RPi and back in again. The power button at the bottom right quorner has the option to log out. Log back in by typing your username "pi"/enter, then your code "raspberry"/enter...

To gain root access for the SPI so that you dont have to use the "sudo" command in front of every bit of code, I followed this guide, which tells you to do this:


Finnish by remove and reattach the power cable to restart.

Python 3

Now it's time to install python3 which is needed to run the code. Open a terminal and run these commands:


Now to use quick2wire with python, you need the python API which can be downloaded from this site. Download it, and unpack it to a location on you SD-card (no installation required!)

Setting up the hardware

The setup is very straight forward. Connect the wires like this (see red circles in the picture):
RPi GPIO9     (Pin 21)    to RF  ( MISO )
RPi GPIO10   (Pin 19)    to RF  ( MOSI )
RPi GPIO11   (Pin 23)    to RF  ( SCK )
RPi GPIO8     (Pin 24)    to RF  ( CSN )
RPi GPIO18   (Pin 12)    to RF  ( CE )
RPI 3.3V        (Pin 17)    to RF  ( VCC/3.3V )
RPi Gnd         (Pin 25)    to RF  (GND)
(IRQ-pin on nRF is not used in this example)

nRF24L02+ (top view)


nRF24L01 (only one of each VCC and GND-pin has to be connected!)


I think the CE-pin should work on a different pin (it would be convenient to put it on GPIO25) since it doesn't have anything to do with the SPI, but I haven't tried that yet...

The python programs

I got the basic nrf python code from this program (plus a lot of help with the steps above) from Jussi Kinnunen, all creds to him!
Download  my version of the program, as well as my TCP-server program. which is well commented to make it easy to understand. You see the codes below:

nRF24L01p.py


TCP_Server.py



To run the program, create a third file, and name it to something like "a.sh", the ending "sh" means shell file, which is runnable. Fill the file with these commands:
(change the path "/home/pi/" to where you have the quick2wire-folder!)

This will make sure the path is imported every time the script is run! If anyone knows how to permanently add the path, please tell me in the comment field!

To run the program, all you have to do now is to set the working path in the terminal to where you store the a.sh-file with the cd-command and type


This is a screenshot from the code running in the terminal:































As you can see, the program starts by asking if you want to run it as "rx" (Receiver) or "tx" (transmitter). I chose transmitter by typing "tx"/enter.
Then it prints out all the registers that the code changes (or sets) and then starts a server loop in a background thread (TCP).
When this is done the program waits for either the user to type in data to send (3 bytes in my example), or for a command from the TCP-server.
In the picture above, you can see that i started by typing "123" on the keyboard, which was sent to the nRF and broadcasted to any nRF-receiver with address 0x12. As you can see the STATUS register after the broadcast tells me that the transmission failed (0x1E), since i don't have a receiver running at the moment... a working transmission would give the result "0x2E" (when the EN_AA is turned on)!

The second thing I did was to send the command "300" to the TCP-server from my home-made android application (can be sent form any TCP-client with the right port number). The TCP-server then calls the nRF24L01p.py and transmits the data like it was inputted with the keyboard.

If a (large) error shows up when you try to run the code, which tells you something about "cannot export the pin number 18 because it is already exported", run the following command in the terminal:



If an error shoes up that tells you that the address is occupied, the TCP-server has not closed yet, and you have to sit down and wait for up to 1min before you try again (I know you can change this timing, but haven't come so far)


If you have any questions or just want to tell me what you think of the blog, just give me a comment in the comment field underneath...
/Kalle

Monday, April 8, 2013

Tutorial - nRF24L01 and AVR

To start out with, you have to know that I am just a hobby programmer so if you find errors or possible improvements in my code, please give me a note so that I can correct them.

If you have read my last blog posts IR-RF remote control and Temperature based flow regulator you have noticed i like to add wireless control to my components. In this tutorial i will describe how i managed to get the nRF24L01 module to work with AVR microships like the Atmega88 (28pin), ATtiny26 (20pin) and ATtiny85 (8pin), since almost all of the tutorials out there are aimed at the Arduino users.

The nRF24L01 module is an awesome RF module that works on the 2,4 GHz band and is perfect for wireless communication in a house because it will penetrate even thick concrete walls. The nRF24L01 does all the hard programming fore you, and even has a function to automatically check if the transmitted data is received at the other end.
There are a couple of different versions of the nRF-family chips and they all seem to work in a similar way. I have for example used the nRF905 (433MHz) module with allmost the same code as I use on the nRF24L01 and the nRF24L01+ without any problems. These little modules has an impressive range, with some versions that manages up to 1000 m (free sight) communication and up to 2000 m with a biquad antenna.

nRF24L01 versus nRF24L01+ 
The (+) version is the new updated version of the chip and supports data rate of 1 Mbps, 2 Mbps and a "long distance mode" of 250 kbps which is very useful when you want to extend the broadcast length.
The older nRF24L01 (which i have used in my previous posts) only support 1 Mbps or 2 Mbps data rate.
Both the models are compatible with each other, as long as they are set to the same data rate. Since they both costs about the same (close to nothing) I would recommend you to buy the + version!



The module
An ebay search on "nRF24L01" shows that there are many different versions of the modules that has the nRF24L01(+), and I have read that some of them are better then others due to better grounding and so on. But if you are after the long-range ones, make sure it has the + sign, and buy one with an extended antenna like this one:




Part one - Setup
Connection differences
The nRF24L01 module has 10 connectors and the + version has 8. The  difference is that the + version instead of having two 3,3 V and two GND, have its ground (the one with a white square around it) and 3,3 V supply, next to each other. If changing module from a new + version to an old one, make sure not to forget to move the GND cable to the right place, otherwise it will shorten out your circuit.

Here is a picture of the + version (top view), where you can see all the connections labeled. The old version has two GND connections at the very top instead of at the down right corner. 

Power supply (GND & VCC)
The module has to be powered with 3,3 V and cannot be powered by a 5 V power supply! Since it takes very little current I use a linear regulator to drop the voltage down to 3,3 V.
To make things a little easier for us, the chip can handle 5 V on the i/O ports, which is nice since it would be a pain to regulate down all the i/O cables from the AVR chip.

Chip Enable (CE)
Is used when to either send the data (transmitter) or start receive data (receiver).
The CE-pin is connected to any unused i/O port on the AVR and is set as output (set bit to one in the DDx register where x is the port letter.)
Atmega88: PB1, ATtiny26: PA0, ATtiny85: PB3


SPI Chip Select (CSN)
Also known as "Ship select not". The CSN-pin is also connected to any unused i/O port on the AVR and set to output. The CSN pin is held high at all the time except for when to send a SPI-command from the AVR to the nRF.
Atmega88: PB2, ATtiny26: PA1, ATtiny85: PB4

SPI Clock (SCK)
This is the serial clock. The SCK connects to the SCK-pin on the AVR.
Atmega88: PB5, ATtiny26: PB2, ATtiny85: PB2


SPI Master output Slave input (MOSI or MO)
This is the data line in the SPI system.
If your AVR chip supports SPI-transfere like the Atmega88, this connects to MOSI on the AVR as well and is set as output.

On AVR's that lacks SPI, like the ATtiny26 and ATtiny85 they come with USI instead, and the datasheet it says:

"The USI Three-wire mode is compliant to the Serial Peripheral Interface (SPI) mode 0 and 1, but
does not have the slave select (SS) pin functionality. However, this feature can be implemented
in software if necessary"

The "SS" refered to is the same as "CSN"
And after some research i found this blog that helped me allot.

To get the USI to SPI up and running I found out that I had to connect the MOSI pin from the nRF to the MISO pin on the AVR and set it as output.
Atmega88: PB3, ATtiny26: PB1, ATtiny85: PB1


SPI Master input Slave output (MISO or MI)
This is the data line in the SPI system.

If your AVR chip supports SPI-transfere like the Atmega88, this connects to MISO on the AVR and this one stays as an input.

To get it working on the ATtiny26 and ATtiny85, i had to use USI as mentioned above. This only worked when I connected the MISO pin on the nRF to the MOSI pin on the AVR and set it as input and enable internal pullup.
Atmega88: PB4, ATtiny26: PB0, ATtiny85: PB0


Interrupt Request (IRQ)
The IRQ pin is not necessary, but a great way of knowing when something has happened to the nRF. you can for example tell the nRF to set set the IRQ high when a package is received, or when a successful transmission is completed. Very useful!

If your AVR has more than 8 pins and an available interrupt-pin i would highly suggest you to connect the IRQ to that one and setup an interrupt request.
Atmega88: PD2, ATtiny26: PB6, ATtiny85: -



Part two- Programming
Here i will explain the c-program that runs on the AVR-chip. You can find a working copy of my code here (easier to copy and paste from).

Includes
I have included these lines to get my code to work:








You can see that i am importing a file called nRF24L01.h. This is a small library that defines the registers of the nRF so that i for example can call register "STATUS" instead of the register "0x07"... Just copy the text in the link and paste it into a file that you name "nRF24L01.h" and put it in the root of your folder.

Defines
To make the code cleaner i also put these definitions in the "nRF24L01.h"-file:







And also add the defines:
#define W 1
#define R  0



SPI
Initialization
The nRF chip communicates with the AVR-chip using SPI which has to be initialized in the AVR according to its datasheet. Here is the initializing code for Atmega88:















ATtiny26:
















ATtiny85:
















Communication
Now to send and receive a byte from the nRF with the SPI all you have to do is to use this function:
Atmega88 (SPI):
ATtiny(26 & 85) (USI as SPI):















I don't think it matters if you send a char or an integer, this is just how i got it to work... Note that these functions always returns something, this returned message is only cared fore when reading data from the nRF (a more appropriate name of the function might be "Write_Read_Byte_SPI").

nRF24L01(+) communication
Now to the fun part...

How it works
1) The nRF starts listening for commands when the CSN-pin goes low.
2) after a delay of 10us it accepts a single byte through SPI, which tells the nRF which bytes you want to read/write to, and if you want to read or write to it.
3) a 10us delay later it then accepts further bytes which is either written to the above specified register, or a number of dummy bytes (that tells the nRF how many bytes you want to read out)
4) when finished close the connection by setting CSN to high again.

Reading bytes from nRF
To start off, make sure your SPI communication is working by reading out something from the nRF. Reading a register on the nRF is accomplished by this function: (all of my example codes is for Atmega88, just change to the right port and pin number for the CSN and CE to get it to work on ATtiny as well)













I recommend you to start out by reading the STATUS register like this:

USART
If you have an AVR that supports USART like the Atmega88, i highly recommend you to use that as a way of sending the data back to the computer with this little friend... (I have written a small tutorial in the subject)



This is done simply by calling the function like this:



If you like me have a function called "USART_Transmit(uint8_t data)"
The usart should send 0b00001110 (or 0x0E) to the computer since it is the preset configuration of the STATUS registry (see the end of this blogpost).

LED
If you are using an ATtiny it lacks the USART and thereby the ability to write things back to the computer, then you can use a more hardcore way using an LED to turn on if the STATUS register is set correctly. Since the bites in the STATUS (0x07) register are preset to 0b00001110 (or 0x0E) you can test if this is true by this function:








Make sure you remember to first set the LED-pin to output: DDRB |=(1<<5);

If you are using an 8-pin ATtiny like the ATtiny85, there is not a single free pin on the chip to put the LED on, so i think a good idea would be to use the MISO-pin as a temporary LED output (since it is an output already and the SPI is not in use at the moment). Attach the LED to the PB1 and via a resistor to GND, then in the if-function above change the port number to 1. I haven't tested this my selves but i doubt it would cause any problem to the SPI-connection.

Writing bytes to the nRF
Now it's time to send a command to the nRF, this is done almost the exact same way as the reading command with this function:











If the register holds more then one byte, the TX_ADDR-byte for example holds five bytes, then you have to send them one at a time after each other with 10us delay in between. This makes the function a bit more complicated since C-code is unwilling to pas arrays of integers into functions as is.

I also wanted to clean up my code a bit, so I decided to make one function that I can use to both read and write to the nRF. The function should also accept an array of integers and be able to return an array of integers. This is the result:
































The most confusing thing whit this function is the W_TX_PAYLOAD in the if-statement... The thing is that when you want to write bytes to the W_TX_PAYLOAD you cannot add the W_REGISTER as you normally does when you want to write to a register. Have a look at the registry setup at the very bottom of this blog post, and you will see that the W_REGISTER and the W_TX_PAYLOAD is in the same "top level" of the registers. The same goes for the TX_FLUSH registers...

Now here is some examples that shows how to use the function:


To come back to the W_TX_PAYLOAD, when you want to ad the payload to the nRF, you simply use the "R" instead of the "W" to trick the WeiteToNrf-function to not add the W_REGISTER.

Setting up nRF24L01(+) 
Now it is time to setup the nRF for your specifications. In the example codes, I will send a 5 byte payload with the nRF. This is easily changed to a 1-32 byte payload by changing a bit in the initialization step of the nRF (see below)... This is how i usually set it up for simple communication between two nRF's:
















































In the code above, i missed a very important setting (if using EN_AA) that sets the number of retries and the retry delay like this:




Add these lines in the function above to set the number of retries to 15 and the delay to 750us... the delay has to be greater than 500us if you are in the 250kbps mode, or if the payload is greater than 5bytes when in 1Mbps-mode or a payload greater then 15bytes when in 2Mbps mode. Note that the default value of this is only 250us, and will therefore cause trouble when in 250kbps mode and with bigger payloads!

As you can see i decided to make it a transmitter this time. This is easily changed in the code by first delay 50ms, to make sure the nRF is in sleep mode, then send 0x1F to the CONFIG register, than make it wait 50ms again before the first receive-command.

Transmit data
When in transmitting mode this is the function that sends your payload:













And you call the transmit function like this: (now i just send 0x93 five times in a row, you can fill the array with any bytes you want to send)








Receive data
When in receiver mode this is the function that listens and receives your data:









 After every received/transmitted payload the IRQ's in the nRF has to be reset in order to receive/transmit next package. This is done like this:











Verify transmitted/received data using Interrupt
If you have more than an 8-pin AVR, I say use an interrupt to get triggered when data is successfully received or transmitted. INT0 interrupt is setup like this:

First you have to initialize the interrupt like this on Atmega88:










And initialization on ATtiny26:










Interrupt caused when receiving data
Setup a function triggered by the corresponding vector (INT0) at the very bottom of your code like this: (the global array "*data" is at the very top of my code...) And here i use it to send the received payload to the computer by usart:




















Interrupt caused on transmission success
Use the same interrupt function when transmitting data but change its content to just flash the LED to tell you that transmission completed, or you can tell the nRF to switch for a receiver, if what you just sent was a question to a receiver that in turn changed to a transmitter to return your call.

When you use interrupt, it is crucial that you enables the external interrupts by the command sei(); This should be done before the receive_payload, and transmit_payload is used.

Verify transmitted received data without interrupt
If your chip lacks interrupt or if you ran out of free interrupt pins, you can manually check if the IRQ-flags are set in the status register after every time you either transmit data, or run the receive_payload function (before the reset function!!!)

Transmitting
The easiest when you want to see if transmission succeeded or failed, is to check if the MAX_RT is set which mean it failed. This is done like this:


Then you know you have to resend the package.... i usually put this statement in a while loop that loops untill the package is received!

Receiving
Do the same check to see if no data is received by checking the RX_DR-bit (data-ready) like this:



Or it might be a better idea to check if data is received by changing "!=" to "==", if not, you start the receive_payload function again!

Code overview














































If you are using the devise as a transmitter, I usually have an USART-interrupt function called "ISR(USART_RX_vect)" at the very bottom that triggers when the computer sends something to the microchip. In the usart interrupt vector it then calls transmit_payload with the data received from the usart.

If you don't use usart, in the main while loop, send the data as described above "Transmit data".

If you don't use the  INT0-interrupt to check if the transmission succeeded  put an if-statement in the main while loop to check whether the correct IRQ flag (nr 4) in the STATUS register is cleared as described earlier.

If your chip is in receiver mode, in the main while loop, i usually call:
while(1)
{
       reset();
       receive_payload();
}

And if i don't have an interrupt, followed by an if-statement to see if anything was received by checking if the correct IRQ flag (nr 6) is set!

Long range mode
If you have the + version, than you can set the RF_SETUP byte to 0x27 instead of 0x07, which will enable the 250 kbps mode (long range) on full power, and i also recommend you to read this tutorial on how to build an amplifying biquad antenna. (remember to set the EN_AA-delay to at least 500us as described above)

Registers
This is straight from the datashet of the older version of the nRF24L01 (i find it easier to read thean the + version)

This is the layout of the "top level" registers as i call them:



And here are all the other registers and there configurations:



I hope you enjoyed my tutorial...
as always, if there is questions there might be an answer in the comment field.
/Kalle

Wednesday, April 3, 2013

Temperature based flow regulator

Since almost none of the readers of this blog understand Swedish, I decided to go international and start writing in English.

In this post I will cover the construction of a temperature based flow regulator that some friends and I have built in order to make sure that the wort temperature is cooled from boiling point to 25°C in our micro brewery. This is important to make sure that the yeast is not killed when added. Since i have some experience in wireless communication we figured it would be cool to make the regulator remotely controlled from an android application.

The setup is kind of similar to my previous project and is built around two Atmega88 with wireless communication through two nRF24L01 adapters (see my tutorial covering the nRF24L01 and AVR).

Regulator setup
- Power supply, 5V 2A USB-power supply (Powerful phone charger that both supplies the Atmega88 and drives the motorized ball valve)

Atmega88, the "computer", a microcontroller that takes and gives commands.

- nRF24L01, 2,4 GHz module that sets up a wireless link with the other nRF-module attached to the computer.

- LM35, Temperature sensor 10mV/°C scale factor and +-0,5°C accuracy.

- Motorized ball valve, 12V (DC-motor works perfectly on 5V, just a bit slower)

- H-bridge, to drive the ball valve DC-motor in ether direction (H-bridge described well here). The components used are two IRF540N (N-channel) and two IRF9540N (P-channel) mosfets.

- Prototype PCB, to solder everything on.

- Heat-exchanger 44kW

- Garden hose connections female, male.


Construction
Armed with a soldering iron, the process went fairly quick and the result is shown below.


The top corner is where the motorized ball valve is connected, and powered via the H-bridge (the fore black mosfets). The temperature probe is attached in the left corner, and the RF-module is the green to the left. The black thing next to the RF-module is a linear regulator that converts the 5 V to 3,3 V since it cannot work with 5 V. When the motor on the valve starts, it produces a huge voltage drop in the system, and therefore it is necessarily to put a large capacitor on the power supply. 


For anyone that wants to construct a H-bridge, i found a great way of soldering them together as you can see at the very top of this picture. Put the two P-channel ones at the back row, and the two n-channel ones in the front row so they line up. Then on each p-channel mosfet you bend the two right connectors (seen from underneath) and solder them to the n-channel ones which you can cut off. For each pair of p+n-channel mosfet you then connect the right rail to respective i/o port on the micro controller, and the middle one to respective DC-motor connection. The  left connectors of the P-channel mosfets connects to 5V, and the left connectors on the N-channel ones connects to ground.



The temperature measurements is done in the hose connector where the wort leaves the heat-exchanger. It looks a bit messy, but at least it does not leak and the quick connector still works! ;)



Setup



The heat exchanger is used as a counter current flow type, and is regulated by controlling the flow of the cold tap-water outlet, so that the temperature of the wort outlet matches its predefined value.


This is an overview of the complete setup of the system (the +-2°C should be -+0,5°C). The home-made module attached to the computer is a similar unit as the regulator, but instead of USB power, temperature probe and dual H-bridge it is connected to the computer via a USB to COM adapter like this one: (i have a small tutorial about USART and Atmega88 here)




Programming

The android application is communication via TCP (wifi or 3G) and is controlled by the same visual basic program i built in the last post to control the ultimate IR-RF remote control:


Screenshots of the android application:


The user can tap on the picture representing the heat exchanger above and set a new temperature for the regulator to aim for. The current temperature is automatically updated every 2 s where it now says "- °C". The two other temperature fields are not up and running at the moment, but in the future they are suppose to regulate the temperature of the boiler and the mash-tun.


If anyone have questions about the build or want me to explain my projects further, please just write a comment!