2013-04-27

WiFly AdHoc UDP Server

The WiFly

After many tries, I finally got the WiFly module to work as a AdHoc server, and to serve NMEA data via Wi-Fi, using the UDP protocol.

I want to configure the WiFly module to act as a UDP server using a terminal connection. I use Putty, connect the WiFly using the USB connection break-out board, and fire up the terminal, then type $$$ to enter command mode.

First I reset the WiFly back to factory settings:


factory RESET
save
reboot

Then I configure the WiFly module using the following commands:


set wlan join 4
set wlan channel 1
set wlan ssid MyArduino
set wlan tx 6
set uart baudrate 38400
set ip address 192.168.1.1
set ip netmask 255.255.255.0
set ip dhcp 0
set ip protocol 1
set ip remote 50000
save
reboot

Please notice that I am using baudrate 38400, as I will be sending AIS data later via this channel. It means that if you re-enter command mode via a terminal emulator, you will need to connect on 38400 baud.
I am using port 50000 as my UDP port. I will be configuring the other Wi-Fi device using static IP, so I  disable DHCP.

The Arduino

I insert the WiFly module back into my Arduino setup.

I have already put together a sketch that multiplexes incoming NMEA data, and outputs this data using the UDP protocol, on port 50000. See my previous posting for source code on this sketch.

The iPhone

On my iPhone, I see and connect to the "MyArduino" WiFi network. I click on the right blue arrow, and specify "Static" IP Address. I then type in the following configuration:
  • IP Address:  192.168.1.1
  • Subnet Mask: 255.255.255.0
  • Router: 192.168.1.1
I leave the other fields blank.

I use a WiFi UDP Test Tool app downloaded from the App Store to monitor incoming UDP data. It shows that the NMEA data is flowing nicely via the UDP protocol all the way from my GPS, via my Arduino to my iPhone.


2013-04-02

Multiplexing serial port NMEA sentences, and streaming to WiFi

In order to connect the GPS together with the VHF/AIS radio, and bringing the data to my iPad, I want to multiplex the incoming NMEA sentences to one single UDP stream.

The code below listens for data on the Arduino Mega's Serial1 and Serial2 ports (can easily be expanded to Serial + Serial3 as well). Incoming data is forwarded to the WiFly module, which streams the data using UDP. See previous posting for how to configure the WiFly module.


#include <WiFlyHQ.h>
#include <SoftwareSerial.h>

String _sentenceGPS;
String _sentenceVHF;
boolean _completedGPS;
boolean _completedVHF;
SoftwareSerial _wiflySerial(10, 11);
WiFly _wifly;

void setup()
{
  // Serial1: Baudrate=4800, Receive=GPS, Send=n/a
  _sentenceGPS.reserve(128);
  _completedGPS = false;
  Serial1.begin(4800);

  // Serial2: Baudrate=38400, Receive=AIS+DSC, Send=GPS
  _sentenceVHF.reserve(128);
  _completedVHF = false;
  Serial2.begin(38400);
  
  // WiFly module
  _wiflySerial.begin(38400);
  _wifly.begin(&_wiflySerial);
  _wifly.setIpProtocol(WIFLY_PROTOCOL_UDP);
}

void loop()
{
  RunMultiplexer();
}

void RunMultiplexer()
{
  char s[128];
  if (_completedGPS)
  {
    memset(s, 0, 128);
    _sentenceGPS.toCharArray(s, 128);
    _wifly.sendto(s, "255.255.255.255", 50000);
    Serial2.print(_sentenceGPS);  // Write GPS sentence to VHF
    _sentenceGPS = "";
    _completedGPS = false;
  }
  
  if (_completedVHF)
  {
    memset(s, 0, 128);
    _sentenceGPS.toCharArray(s, 128);
    _wifly.sendto(s, "255.255.255.255", 50);
    _sentenceVHF = "";
    _completedVHF = false;
  }
}

void serialEvent1()
{
  if (Serial1.available())
  {
    char c = Serial1.read();
    _sentenceGPS += c;
    if (c == '\n')
    {
      _completedGPS = true;
    }
  }
}

void serialEvent2()
{
  if (Serial2.available())
  {
    char c = Serial2.read();
    _sentenceVHF += c;
    if (c == '\n')
    {
      _completedVHF = true;
    }
  }
}


Right now the incoming GPS data on Serial1 is forwarded to the outgoing Serial2 port. I will add parsers and more logic to this - stay tuned! :-)

2013-03-22

Interfacing to the Accelerometer module

I also had a tripple axis accelerometer module laying around from one of my "shopping trips" to Sparkfun a few years ago. It is a so-called BMA180 module. The module is retired at Sparkfun, but there are new replacement modules available that are able to do similar to what I plan to do here.

I googled the module, and found a great summary of a seismograph project that I am using as an example of interfacing and reading data from the module. That project is called Central Nexus Seismograph. Many thanks to the persons at Central Nexus for the help!

I connected the BMA180 module to the Arduino in the following way:
  • Pin VDD:   3.3V
  • Pin GND:   GND
  • Pin INT:     Not used
  • Pin CS:       3.3V
  • Pin SCK:    A5
  • Pin SDO:    GND
  • Pin SDI:      A4
  • Pin VIO:     3.3V
BMA180 Accelerometer connection

BMA180 connections with Arduino Duemillanove

I took the example source code from the Central Nexus Seismograph, but I had to make some adjustments due to some upgrading in the Wire library (some methods had been renamed). 

At Central Nexus they are printing the values as binary data. I changed that to print the raw values as text, just to be able to monitor the results of moving the accelerometer around in an easy manner.


Here is the source code:

/*
Copyright (c) 2010-2011 George Rhoten

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/*
If you need some reference material take a look at the following:

Instrumentation in Earthquake Seismology (Modern Approaches in Geophysics) 
  by Jens Havskov, Gerardo Alguacil

BMA180 digital, triaxial acceleration sensor data sheet
  by Bosch Sensortec

The original source code can be found at:
http://www.centralnexus.com/seismograph/details_bma180.html
*/
#include <Wire.h>

// ADXL345
#define DEVICE ((byte)0x40)    //BMA180 device address

#define DATA_X0 0x02 //X-Axis Data 0
#define DATA_X1 0x03 //X-Axis Data 1
#define DATA_Y0 0x04 //Y-Axis Data 0
#define DATA_Y1 0x05 //Y-Axis Data 1
#define DATA_Z0 0x06 //Z-Axis Data 0
#define DATA_Z1 0x07 //Z-Axis Data 1
#define DATA_TEMP 0x08 //Temperature
#define SOFT_RESET 0x10 //soft_reset
#define SOFT_RESET_VAL 0xB6 //soft_reset value to reset

#define AXIS_SHIFT 2 //Amount to right shift data. The first 2 bits are status bits.

// USGS suggests rates of 20, 40 or 200 samples per second for natural earthquakes.
//#define DELAY_RATE 9990 //~100Hz
//#define DELAY_RATE 6600 //~150Hz
#define DELAY_RATE 4993 //~200Hz

// LED
#define COMMON_ANODE 4
#define RED_PIN 3
#define BLUE_PIN 5
#define GREEN_PIN 6
#define LED_VALUE_SHIFT 4

#define GRAVITY 255
#define ANALOG_MAX 0xFF
#define USE_LED 0

/**
 * The value in relation to 0xFF within the range value.
 */
static byte pinMagnitudeRangeToTop(byte range, int value) {
  if (value < 0) {
    value = -value;
  }
  if (value > 255) {
    return (byte)ANALOG_MAX - range;
  }
  return (byte)ANALOG_MAX - (byte)(((float)value / (float)GRAVITY) * (float)range);
}

//Writes val to address register on device
static void writeTo(byte address, byte val) {
  Wire.beginTransmission(DEVICE); //start transmission to device 
  Wire.write(address);        // send register address
  Wire.write(val);        // send value to write
  Wire.endTransmission(); //end transmission
}

//reads num bytes starting from address register on device in to buff array
static void readFrom(byte address, byte num, byte *buff) {
  Wire.beginTransmission(DEVICE); //start transmission to device 
  Wire.write(address);        //sends address to read from
  Wire.endTransmission(); //end transmission

  Wire.requestFrom(DEVICE, num);    // request num bytes from device
  num = Wire.available(); //device may send less than requested (abnormal)
  while(num-- > 0) {
    *(buff++) = Wire.read(); // receive a byte
  }
}

/**
 * Writes val to address register on device if it's different from
 * the current value. This decreases the wear and tear on the EEPROM.
 */
static void writeOptionallyTo(byte address, byte val, byte mask) {
  byte value = 0;
  readFrom(address, sizeof(value), &value);
  if ((value & mask) != (val & mask)) {
    // Keep the unmasked values, and changed the masked values.
    writeTo(address, (value & ~mask) | (val & mask));
  }
}


void setup()
{
  Wire.begin();          // join i2c bus (address optional for master)
  Serial.begin(38400);  // start serial for output
  Serial.flush();

  // Wait for readings to settle down.
  // 10ms Pause is required to write registers.
  delay(15);

  writeOptionallyTo(0x0D, 0x10, 0x10); // Enable register write

  // I can't figure out how to modify the EEPROM.
  // So we reset upon startup.

  // Change calibration values based on new mode.
  // Each chip will need their own calibration.
  // Note: some offset values affect the spikiness of the values. You should test your values.
  writeOptionallyTo(0x3A, 0x76, 0xFF); // original offset MSB z=0x78 when mode_config=0
  writeOptionallyTo(0x39, 0x5C, 0xFF); // original offset MSB y=0x60 when mode_config=0
  writeOptionallyTo(0x38, 0x6D, 0xFF); // original offset MSB x=0x70 when mode_config=0
  writeOptionallyTo(0x36, 0x7C, 0xFF); // original offset LSB z=0x4, y=0xC when mode_config=0
  // Need a range of existing gravity + 1 G movment to get up to a 9.0M earthquake.
  writeOptionallyTo(0x35, 0xC4, 0xFF); // original offset LSB x=0x1, range+12bit=0x4 when mode_config=0

  writeOptionallyTo(0x30, 0x01, 0x03); // Change mode_config to lower noise mode
  writeOptionallyTo(0x21, 0x00, 0x04); // Turn off adv_int
  
  /* Earthquakes have a nominal frequency range of 0.001–50 Hz. The P and S
   * waves of >2.0M earthquakes usually have a frequency of 0.1-10 Hz. The
   * higher frequencies are attenuated by the bedrock. So you need to be
   * close to the epicenter to measure the higher frequencies. In order to
   * accurately record the lower frequencies, the bandwidth measurement must
   * be lowered in the sensor. When equal to 0x8 or 0x9, the gravity will be
   * cancelled out.
   */
  writeOptionallyTo(0x20, 0x20, 0xF0); // Change bandwidth. 0x2=40Hz 0x3=75Hz Originally 0x4

  writeOptionallyTo(0x0D, 0x00, 0x10); // Disable register write for protection

#if USE_LED
  //3 color LED
  pinMode(COMMON_ANODE, OUTPUT);
  digitalWrite(COMMON_ANODE, HIGH);

  pinMode(RED_PIN, OUTPUT);
  digitalWrite(RED_PIN, HIGH);

  pinMode(GREEN_PIN, OUTPUT);
  digitalWrite(GREEN_PIN, HIGH);

  pinMode(BLUE_PIN, OUTPUT);
  digitalWrite(BLUE_PIN, HIGH);
#endif
}

void loop()
{
  // 2 byte endian marker
  // 6 byte buffer for saving data read from the device
  // 2 byte checksum in case there is a reset in the middle of a packet.
  int axis[5] = {0x8081, 0, 0, 0, 0};

  // There are 1,000,000 microseconds per second,
  // and we want to sample about 200 per second.
  // This gives us about the right rate with the rest of the overhead.
  delayMicroseconds(DELAY_RATE - (int)(micros() % DELAY_RATE));
  
  // Each axis reading comes in 14 bit resolution (2 bytes little endian).
  readFrom(DATA_X0, 6, (byte*)(axis+1)); //read the acceleration data

  // Remove status and 0 bits
  axis[1] = axis[1] >> AXIS_SHIFT;
  axis[2] = axis[2] >> AXIS_SHIFT;
  axis[3] = axis[3] >> AXIS_SHIFT;

  // Calculate checksum.
  axis[4] = axis[1] + axis[2] + axis[3];
  // Write whole packet.
  //Serial.write((byte *)axis, sizeof(axis));

  Serial.print(axis[1]);
  Serial.print("  ");
  Serial.print(axis[2]);
  Serial.print("  ");
  Serial.println(axis[3]);

#if USE_LED
  analogWrite(RED_PIN, pinMagnitudeRangeToTop(0xFF, axis[1]>>LED_VALUE_SHIFT));
  analogWrite(GREEN_PIN, pinMagnitudeRangeToTop(0xFF, axis[2]>>LED_VALUE_SHIFT));
  analogWrite(BLUE_PIN, pinMagnitudeRangeToTop(0xFF, axis[3]>>LED_VALUE_SHIFT));
#endif
}


After compiling and uploading, I set the Arduino Serial Monitor to 38.400 baud, and got the following results:

I am now able to interface to the accelerometer!

Interfacing the GPS module

The GPS I plan to interface is an EM-406A module that I had laying around. I want to mount the GPS module away from the Arduboat box, so I had to extend the cable. I cut off the connector cable socket in the other end of the GPS, and soldered on a shielded extension cable with a length of about 150 cm.



Before integrating the GPS with the Arduino Mega, I wanted to test it on my Arduino Duemillanove. I connected the pins on the GPS to my Arduino Duemillanove in the following way:
  • Pin 1+5: Ground (I connected this also the the cable shielding)
  • Pin 2: +5V
  • Pin 3: A4
  • Pin 4: Not used
  • Pin 5: Not used
  • Pin 6 (grey cable): Not used
I then created a small Arduino that simply prints the incoming data to the screen:

#include <SoftwareSerial.h>

SoftwareSerial _gpsSerial(4,5);

void setup()
{
  Serial.begin(4800);
  _gpsSerial.begin(4800);
}

void loop()
{
  if (_gpsSerial.available())
  {
    Serial.write(_gpsSerial.read());
  }
}

I uploaded the program to the Arduino Duemillanove, and started the Arduino Serial Monitor, ensuring that the baudrate was set to 4800 in the monitor window. I placed the GPS module near my window, to ensure a good receiption. In the beginning I received almost empty NMEA strings from the GPS module, but after a few minutes the GPS was calibrated correctly, and started outputting data as expected:



2013-03-06

Arduino and WiFi

As part of my first goal to integrate my on-board electronics with my iPad/iPhone, I have to come up with a solution on how to makes my marine gear talk Wi-Fi.

I have spent quite a few hours in the past days to figure out how to configure the Arduino with WiFi. This post is quite technical, and summarizes all steps to achieve the goals that I had. There was a bit of trial-and-error involved, but I got things working in the end.

I am currently using an old Arduino Duemilanove, connected to a Roving Networks RN-XV WiFly module, with an old XBee Explorer Regulated in between. I got all this stuff from Sparkfun.com a few years ago. It would probably be easier to buy the WiFly Shield today, but I had these modules laying around, so I decided to use them.

With the stuff mentioned above, I have connected a serial signal, currently coming through my USB cable from my PC to my Arduino, to a UDP broadcast over WiFi.

The longer plan is to use this as a basis for my NMEA hub, but extend it with a new Arduino MEGA controller. I will wait with the connection until I got everything working.

I also have a XBee Explorer USB that I have been using to configure my WiFly module.
XBee Explorer USB
The WiFly module is configured by connecting the WiFly module with the XBee Explorer USB, plugging the XBee Explorer USB to the PC, and running Putty as a configuration terminal software.

On my computer, the XBee Explorer USB shows up on COM3, and I connect to that with the default speed of 9600 bps.

Once connected with Putty, I ran the following commands, taken from the WiFly manual:

factory RESET
save
reboot
set wlan ssid <your-wifi-ssid>
set wlan pass <your-wifi-password>
save
reboot 

I am using Port Peeker for monitoring UDP packages on my network: Install, then start Port Peeker, and select the UDP protocol, and port 55555, which is the default WiFly heartbeat port. You should see something like this, that indicates that your WiFly is operating as expected:


---- 06.03.2013 22:33:07.562
0000   28 CF DA B7 9D 63 01 1F 07 D0 00 00 06 6B 0B ED   (....c.......k..
0010   0D 01 54 69 6D 65 20 4E 4F 54 20 53 45 54 00 00   ..Time NOT SET..
0020   57 69 46 6C 79 20 56 65 72 20 32 2E 33 30 2C 20   WiFly Ver 2.30, 
0030   31 30 2D 32 36 2D 32 30 31 31 00 00 57 69 46 6C   10-26-2011..WiFl
0040   79 2D 45 5A 58 00 00 00 00 00 00 00 00 00 00 00   y-EZX...........
0050   00 00 00 00 00 00 00 00 00 00 00 09 21 00 00 00   ............!...
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00         ..............  

I continued with Putty, and added the following commands:

set ip proto 1
set uart baudrate 38400
save
reboot

This puts WiFly into UDP only mode, and sets the baud rate to 38400, which is the standard baudrate for AIS messages. I intend to run all NMEA messages on 38400 bps.

I removed the WiFly module from the USB adapter, and inserted it into the XBee Explorer Regulated module, that I had to solder in a 10 kOhm resistor between the pins indicated in the picture below to make the WiFly module work with the XBee Explorer Regulated.


Modification of XBee Explorer Regulated with 10kOhm resistor
The connections are done in the following way:
Arduino with WiFly

Arduino          XBeeExplorerRegulated
5V         ->    5V
GND        ->    GND
D2         ->    DOUT
D3         ->    DIN

In the Arduino development tool, I have created a small program that simulates an incoming AIS signal:

#include <WiFlyHQ.h>
#include <SoftwareSerial.h>

SoftwareSerial _wiflySerial(2,3);
WiFly _wifly;

void setup()
{
  _wiflySerial.begin(38400);
  _wifly.begin(&_wiflySerial);
  _wifly.setIpProtocol(WIFLY_PROTOCOL_UDP);
}

void loop()
{
  _wifly.sendto("!AIVDM,1,1,,B,19NWrrP02sbuuuuhM86hA0=n2<0:,0*12\r\n", "255.255.255.255", 50000);
  delay(1000);
}

This small program simply dumps out a hard-coded test NMEA sentence on the WiFly module. I verify that the entire signal flow is working by running Port Peeker again, this time monitoring UDP port 50000. I got the following results, which confirms that everything works as expected:

192.168.1.19 : 2000 Length = 49 bytes
MD5 = 694D4EFC160306E00AA945CD995B9E77
---- 06.03.2013 22:40:39.359
0000   21 41 49 56 44 4D 2C 31 2C 31 2C 2C 42 2C 31 39   !AIVDM,1,1,,B,19
0010   4E 57 72 72 50 30 32 73 62 75 75 75 75 68 4D 38   NWrrP02sbuuuuhM8
0020   36 68 41 30 3D 6E 32 3C 30 3A 2C 30 2A 31 32 0D   6hA0=n2<0:,0*12.
0030   0A       

Hurrah! I now have an Arduino that can output NMEA sentences via Wi-Fi, and broadcasting it using the UDP protocol. This is what I need for the next step...

2013-03-03

Before upgrading

Engine-wise, my Hansvik has a Yamaha 50 hp 4-stroke outboard motor, and is controlled with a Yamaha 703 remote control box. The steering cabling is based on the teleflex system. I have considered upgrading to a hydraulic steering system, but I think that I have to wait a bit with that, as the current system works fine and a hydraulic system is quite expensive...

When I bought my Hansvik back in 2012, it had a single four switch dashboard panel, and everything was hardwired with simple/poor connections. I replaced all electrical cables with traces of oxidation to ensure good connections on the electronic system. I also bought a second switch panel, relocated the original installed it below the original switch panel. I installed a ground connection bus for the (-) negative polarity. The (+) positive polarity is now going through the switch panels, which also provide fuses. I also installed a voltmeter to monitor the battery voltage, to monitor the battery when fishing with with the engine off, and electronics on (echo sounder, music etc.).

This is how the cockpit currently looks after the upgrading (today, March 3rd 2013):



In short, the boat currently has the following electrical equipment installed:

  • Yamaha 50hp 4-stroke engine w/703 remote control box
  • Teleflex steering wheel
  • Electrical switch panels (12 volts)
  • Engine instruments: Trim indicator, RPM indicator, oil/temp lamps
  • Humminbird Wide 100 echo sounder
  • LED instrument lamp, ceiling lightning
  • 4 lanterns according to powerboat system
  • Voltmeter
  • Main switch
  • Level sensor with water pump, directly connected to the battery with an in-line fuse
  • 73 Ah 12V marine lead acid battery

Goals

I have a 15 year old 18 foot powerboat of the Norwegian brand Hansvik, model 18 TF, equipped with a Yamaha 50hp 4-stroke outboard engine. I am in the process of upgrading my boat with modern electronics, to be used for recreational and fishing trips in the Stavanger area in Norway in the summer of 2013.

The projects consists of two main parts:
1. Connecting the on-board electronics to my iPad and iPhone. I plan to use an Arduino Mega as a data hub.

2. Adding a safety & surveillance monitoring module that reports status of my boat to my iPhone. I will base it on Arduino, and has GPS, temperature, water level, accelerometer and PIR sensors.It will send reports on a regular basis using GPRS, and send SMS messages immediately when values go outside defined intervals.