GHI WiFi RN171 and wireless security

Hi,
I’m playing around with my new WiFi RN171 Module and it works fine as http server. Now I would like to implement wireless security like WPA2. I read that the RN171 Module merely works as open network without wireless security. Is it really not possible to implement wireless security or is it only the driver that gives no means to activate it?
Cheers
Roland

According to the datasheet

it supports


It also supports SPI for higher data rates, would be an interesting option too.
I guess the driver is open source (I think I browsed trough the code already).
So It might be not too hard to extend the driver.

Edit:
Here:
http://ww1.microchip.com/downloads/en/DeviceDoc/50002230A.pdf
is the command reference.
The command

```cs]set wlan auth 4[/code

Sets the module to WPA2-PSK.

```cs]set wlan key ...[/code

sets the authentication key

Should really not bee too hart to extend the driver

@ Reinhard Ostermeier -
Hi Reinhard,
thank you for your informations.

You once intended to realize a project with the RN171 Module

https://www.ghielectronics.com/community/forum/topic?id=14875&page=1

Did you get it ready ? I think a wireless solution without data encryption is a no-go.

James mentioned: “Also, bear in mind that the RN-171 is only capable of being an OPEN WiFi network – anything with a password will not work.”

https://www.ghielectronics.com/community/forum/topic?id=14817

Did anybody realize a solution with RN171 and wireless security protocols?

@ RoSchmi - This project is pending, since our potential customer does not need it urgently and we are currently happy to not get it now :whistle:

What I have seen from flying through the datasheet and command ref, it should be really easy to enable security for the RN-171.

I guess James meant that only Open networks are possible in combination with the given driver, which supports really just the basics of RN171.

In fact I get the feeling that most Gadgeteer drivers only support the basic functionality.
I think someone could make some good money by implementing “complete” Gadgeteer and plain NETMF drivers for some of these modules and give some support for that.

Now that I know more about RN171 I guess I would by the plain module (not the Gageteer one from GHI) for the project mentioned above, and implement a nice plain NETMF driver for it. I probably would connect to it by SPI then as well.

So let’s see, who will first or ever get enough time :think:

You could buy mine :whistle:

I can confirm that security works on this module. I use it in both AP mode to setup a private network and I have the module joining my home network, not at the same time of course :slight_smile:

In AP mode I use WPA2, however I do keep the module firmware updated. Updating the firmware is the primary reason I join the module to my home network, so that the module has internet access, I do the update of the module via FTP as described in the documentation.

I currently have the 4.41 firmware loaded, and I would suggest that you upgrade the firmware as it has better support for security. You will need to modify the driver to use some of the updated features.

1 Like

Hi taylorza,
that’s great.
I think it’s not only me who would be grateful to see your code for AP-mode with security and joining the module to the home network to update the firmware.
I already managed to add the driver code in a class that can be included in my projects so that modification can easily be done.
Cheers
Roland

@ RoSchmi - I would gladly share the code, but I just have not really put much time/effort into it to get it into a state that makes it nice and reusable. And when I started using the module it did not look like anyone else was really interested.

Now that there is some traction, I will definitely clean-up the code and get it posted. I do not like my current strategy for the state transitions etc. it can and needs to be more robust.

Here is the code I tested with, using the 4.41 firmware. Do not use the state management code as a reference it is really just a quick and dirty to get the basics going. I did not implement the actual network send/receive, HTTP server etc. I was just using telnet to connect to the device and issue commands etc.


using Gadgeteer;
using Gadgeteer.Interfaces;
using Gadgeteer.Modules;
using Microsoft.SPOT;
using System;
using System.Text;
using System.Threading;

namespace WifiRN171Test
{
    public class WifiRN171 : Module
    {
        private int CommandTimeout = 10000;
        
        private string _newLine = "\r\n";
        private Decoder _decoder = Encoding.UTF8.GetDecoder();
        private string _lastError;

        private DigitalOutput _resetPin;
        private DigitalOutput _rtsPin;
        private State _state;
        private Serial _serial;

        public WifiRN171(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);
            socket.EnsureTypeIsSupported(new char[] { 'U', 'K' }, this);

            _resetPin = new DigitalOutput(socket, Socket.Pin.Three, true, this);
            
            _serial = new Serial(socket, 9600, Serial.SerialParity.None, Serial.SerialStopBits.One, 8, Serial.HardwareFlowControl.NotRequired, this);
            _serial.Open();
      
            _rtsPin = new DigitalOutput(socket, Socket.Pin.Six, false, this);

            ExitCommandMode();

            new Thread(ReadLineThread).Start();
        }

        public void StartAccessPoint(string ssid, int channel,
            string key = "",
            string ipAddress = "192.168.2.1", 
            string gateway = "192.168.2.1",  
            string subnetmask = "255.255.255.0",
            bool enableDhcpServer = true)
        {
            Reset();
            ExitCommandMode();
            EnterCommandMode();
            SendCommand("set sys printlvl 0");
            SendCommand("set wlan join 7");
            SendCommand("set wlan channel " + channel);
            SendCommand("set apmode ssid " + ssid);
            SendCommand("set apmode passphrase " + key);            
            if (enableDhcpServer) SendCommand("set ip dhcp 4");
            SendCommand("set ip address " + ipAddress);
            SendCommand("set ip netmask " + subnetmask);
            SendCommand("set ip gateway " + gateway);
            SendCommand("set ip localport 2000");
            SendCommand("set ip protocol 2");

            ExitCommandMode();
        }

        public void JoinNetwork(string ssid, AuthMode authMode = AuthMode.Open, string key = "")
        {
            Reset();
            ExitCommandMode();
            EnterCommandMode();

            SendCommand("set wlan join 0");
            SendCommand("set wlan channel 0");
            SendCommand("set wlan ssid " + ssid);
            
            if (authMode == AuthMode.Open)    
            {
                SendCommand("set wlan key");
            }
            else if (authMode == AuthMode.WEP128)    
            {
                SendCommand("set wlan key " + key);
            }
            else
            {
                SendCommand("set wlan phrase " + key);
            }

            SendCommand("set ip dhcp 1");
            SendCommand("set ip localport 2000");
            SendCommand("set ip protocol 2");
            
            SendCommand("join", false);
            // This sleep gives the module 30 seconds to join the network
            // when I have better state handling this will not be required.
            Thread.Sleep(30000);
           
            ExitCommandMode();
        }

        public void Reset()
        {
            _resetPin.Write(false);
            Thread.Sleep(100);
            _resetPin.Write(true);
            Thread.Sleep(250);
        }

        public void EnterCommandMode()
        {
            SendCommand("$$$");
        }

        public void ExitCommandMode()
        {
            SendCommand("exit", false);
            _state = State.Network;
        }

        private void WaitForState(State newState)
        {
            var timeout = DateTime.Now.AddMilliseconds(CommandTimeout);

            while (_state != newState && DateTime.Now < timeout)
            {
                if (_state == State.Error)
                {
                    throw new Exception(_lastError);                
                }
                Thread.Sleep(10);
            }
                        
            if (_state != newState)
            {
                throw new Exception("Command timeout");
            }
        }
        
        private void ReadLineThread()
        {
            byte[] byteBuffer = new byte[4];
            char[] charBuffer = new char[2];

            int readIndex = 0;
            int bytesUsed, charsUsed;
            bool completed = false;

            StringBuilder outcome = new StringBuilder();
            int totalRead = 0;

            while (true)
            {
                if (!_serial.IsOpen)
                {
                    Thread.Sleep(100);
                    continue;
                }

                int read = _serial.Read(byteBuffer, readIndex, 1);
                if (read > 0)
                {
                    totalRead += read;
                    int convertIndex = 0;

                    while (!completed)
                    {
                        _decoder.Convert(
                            byteBuffer, convertIndex, totalRead - convertIndex,
                            charBuffer, 0, charBuffer.Length, false, out bytesUsed, out charsUsed, out completed);

                        if (charsUsed > 0) // great, write them down
                        {
                            outcome.Append(charBuffer, 0, charsUsed);

                            //if (outcome.Length >= 6)
                            //{
                            //    outcome.Replace("*OPEN*", "*OPEN*\r\n");
                            //    outcome.Replace("*CLOS*", "*CLOS*\r\n");
                            //}

                            if (outcome.Length >= _newLine.Length)
                            {
                                // Chance for the new line marker being in, however, some characters after it could sneak in as well,
                                // so we need to check for newLine ending on at least charsUsed last positions.
                                // The extra characters could also happen to be line markers, so we can't check from the end of the string.

                                int lastStart = 0;
                                for (int i = System.Math.Max(0, outcome.Length - System.Math.Max(_newLine.Length, charsUsed)); i <= outcome.Length - _newLine.Length; i++)
                                {
                                    if (outcome[i] == _newLine[0])
                                    {
                                        bool newLineFound = true;

                                        for (int nl = 1; nl < _newLine.Length; nl++)
                                        {
                                            if (outcome[i + nl] != _newLine[nl])
                                            {
                                                newLineFound = false;
                                                break;
                                            }
                                        }                                        

                                        if (newLineFound)
                                        {
                                            // the below is a workaround for a bug in NETMF stringbuilder class - used to be string line = outcome.ToString(lastStart, i - lastStart);
                                            string line = outcome.ToString().Substring(lastStart, i - lastStart);
                                            OnLineReceived(line);

                                            lastStart = i + _newLine.Length;
                                            i = lastStart - 1;
                                        }
                                    }
                                }
                                if (lastStart > 0)
                                {
                                    outcome.Remove(0, lastStart);
                                }
                            }
                        } // charsUsed


                        if (bytesUsed > 0) // either completed or an invalid character
                        {
                            convertIndex += bytesUsed;
                        }
                        else
                        {
                            // in this case the decoder does not have any state, and we need more bytes to decode the character
                            if (convertIndex > 0)
                            {
                                // start from the beginning buffer, so that we can get all the required bytes
                                Array.Copy(byteBuffer, convertIndex, byteBuffer, 0, totalRead - convertIndex);
                                readIndex = 0;
                            }

                            if (++readIndex >= byteBuffer.Length)
                            {
                                // this not RFC 3629 UTF-8 decoder and we need a larger buffer
                                byte[] largerBuffer = new byte[byteBuffer.Length * 3 / 2];
                                byteBuffer.CopyTo(largerBuffer, 0);
                                byteBuffer = largerBuffer;
                            }

                            break;
                        }
                    } // completed

                    if (completed)
                    {
                        totalRead = 0;
                        readIndex = 0;
                        completed = false;
                    }
                } // read
            } // main loop
        }

        void OnLineReceived(string line)
        {
            if (line.IndexOf("CMD") == 0 
                || line.IndexOf("AOK") == 0)
            {
                _state = State.Success;                         
            }
            else if (line.IndexOf("ERR") == 0)
            {
                _state = State.Error;
                _lastError = line;
            }
            else
            {
                _state = State.Echo;
            }
            Debug.Print(line);
        }


        private void SendCommand(string command, bool waitForResponse = true)
        {
            if (command != "$$$")
            {
                if (command.IndexOf('\r') < 0)
                {
                    command += "\r";
                }
            }

            var bytes = Encoding.UTF8.GetBytes(command);
            SendCommand(bytes, waitForResponse);
        }

        private void SendCommand(byte[] command, bool waitForResponse)
        {
            _state = State.ExecuteCommand;
            _serial.Write(command, 0, command.Length);
            if (waitForResponse)
            {
                WaitForState(State.Success);
            }
            else
            {
                Thread.Sleep(500);
            }
        }

        enum State
        {
            Network,
            ExecuteCommand,
            CommandMode,
            Success,
            Echo,
            Error,            
            Timeout,
        }        
    }

    public enum AuthMode
    {
        Open = 0,
        WEP128 = 1,
        WPA1 = 2,
        Mixed = 3,
        WPA2_PSK = 4
    }
}

Initialize the module on socket 2 of the Cerberus


  WifiRN171 _wifi;
_wifi = new WifiRN171(2);

Starting the module as an access point, with a pass phrase


_wifi.StartAccessPoint("WifiRN171", 2, "1234567890");

Joining the module to an existing network


_wifi.JoinNetwork("TaylorGuest", AuthMode.Open, "1234567890");

To tinker with other commands you can use the SendCommand method. but you need to read the document for the commands, some return a AOK string when done others do not.

@ taylorza - thank you very much.
I’ll try and report the results.

@ RoSchmi - No problem, I hope this helps and gets you started. Just a few observations,

[ol]I found that I needed to issue a ‘factory RESET’ command on the module before I could get it to work nicely.
After changing between AP and joining the network, you should call the ‘save’ and then ‘reboot’ command. These are some of the interface things I want to clean-up, besides of course actually adding “socket” type support etc.[/ol]

Now you see why I did not share the code, it is very incomplete, but it does work, at least for me.

@ taylorza - You wrote you want to add Socket Type support.
1st, that would really be great
2nd does the RN171 support multiple connections? It doesn’t with the GHI driver.
3rd Any hint how one can add Socket Support for any communication device? Is there some kind of “Plugin” API to do so?

@ Reinhard Ostermeier - It does not support multiple connections, but I wanted to give it an interface that looks similar to working with Sockets. That way code that works with other network devices could be more easily ported to the RN171.

Adding support for the actual sockets will require that you write a native driver in the core NETMF that interfaces with LWIP. Given that the RN171 has the network stack embedded in the device this is not the right approach for the RN171, it does everything for you already.

This is really neat little module in my opinion, it supports TCP, UDP, NTP, HTTP, Infrastructure, Ad-hoc and Access Point networking. It has it’s limitations, but once you get going, it is at least easy to work with.

1 Like

I thought so, but I feared I was missing something.
Thank you

@ taylorza - @ Reinhard Ostermeier
Hi,
it took some time, but I’m back with success.
Combining parts of the GHI driver code, taylorza’s code and the instructions of forum member KG1 I was able to join my home-wirelesse-network with WPA2-PSK, update the RN171 firmware to version 4.41 and then to use the AP-Mode with WPA2-PSK authentication (which did not work with firmware version 4.0).
As you already mentioned a „factory RESET" command was needed before the RN171 was able to join my home wireless network.
Since GHI now unfortunately stopped the RN171 module production, I should better have bought a XBee Adapter Module and a RN-XV WiFly Module which should be easily adapted.

Hi Roland,
After a whole month fighting spambots and restoring my website, I can get back to building robots! How is your project progressing?
Kevin

@ KG1 -
Hi KG1, I’am making progress. Thank you for your helpful instructions. I think I will post a Utility to configure the RN171 when I have it ready. But this must wait for some weeks due to summer holidays in the USA

@ RoSchmi
Hi Roland, hope you are enjoying your holiday. I am most interested to learn of your progress with your project. Please email me when you publish your utility kevin(dot)gordon(at)clear(dot)net(dot)nz