{"version":"1.0","provider_name":"Projects","provider_url":"https:\/\/projects.schneidr.de","author_name":"Gerald","author_url":"https:\/\/projects.schneidr.de\/author\/admin\/","title":"MRF24J40 Bluetooth Bridge","html":"So, I got my wireless bridge working. Here are pictures of my setup, an <a href=\"http:\/\/www.adafruit.com\/products\/746\">Adafruit Ultimate GPS<\/a> module happily sending its location to my Android phone.\r\n\r\n<a href=\"http:\/\/projects.schneidr.de\/files\/2012\/10\/P1150231.jpg\"><img class=\"aligncenter size-large wp-image-561\" title=\"P1150231\" src=\"http:\/\/projects.schneidr.de\/files\/2012\/10\/P1150231-600x450.jpg\" alt=\"\" width=\"584\" height=\"438\" \/><\/a>\r\n\r\nThe Arduino sitting out of my window on the roof is connected to the GPS receiver and one of the two\u00a0MRF24J40MA modules.<!--more--> Both MRF modules are connected up <a title=\"MRF24J40 and Arduino\" href=\"http:\/\/projects.schneidr.de\/2012\/10\/mrf24j40-and-arduino\/\">as described in the previous post<\/a>. I had a little trouble connecting the GPS receiver, because one of the preferred pins on the Arduino was already in use by the wireless module. I couldn't get it to work with other pins over SoftwareSerial, so I connected it to the Serial1 pins of the Arduino Mega. Not sure how I will do that if I end up using a different Arduino for the boat, with only one serial interface. Not sure if it was coincidence, but the problematic pin happend to be Arduinos first interrupt pin. I don't know if the GPS library relies on using interrupts, but this was the only way I found to make it work.\r\n\r\n<a href=\"http:\/\/projects.schneidr.de\/files\/2012\/10\/P1150235.jpg\"><img class=\"aligncenter size-large wp-image-562\" title=\"P1150235\" src=\"http:\/\/projects.schneidr.de\/files\/2012\/10\/P1150235-600x450.jpg\" alt=\"\" width=\"584\" height=\"438\" \/><\/a>\r\n\r\nThe wireless transceiver is sending the data gathered by the Arduino outside to another Arduino, which just forwards the data to the Amarino bluetooth library, finally sending it to the Android phone where I interpret it and for now just show it as a toast message. The communication goes both ways, sending commands from the phone over Bluetooth and the ZigBee connection to the Arduino Mega, which eventually will drive the boat.\r\n\r\nThe only thing I still have to figure out is why the reported position is about 50km off of my actual position.\r\n\r\n<h2>Code<\/h2>\r\nThe Java code for Android is a little too complex to post it here, I'll try to summarize the essential parts.\r\n<pre lang=\"java\">import at.abraxas.amarino.Amarino;\r\nimport at.abraxas.amarino.AmarinoIntent;\r\n\r\npublic class APControlActivity extends Activity {\r\n\tstatic String btBoat = \"00:11:12:xx:xx:xx\";\r\n\tprivate ArduinoReceiver arduinoReceiver = new ArduinoReceiver();\r\n\r\n\t@Override\r\n\tprotected void onStart() {\r\n\t\tsuper.onStart();\r\n\t\tregisterReceiver(arduinoReceiver, new IntentFilter(AmarinoIntent.ACTION_RECEIVED));\r\n\t\tAmarino.connect(this, btBoat);\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onStop() {\r\n\t\tsuper.onStop();\r\n\t\tAmarino.disconnect(this, btBoat);\r\n\t\tunregisterReceiver(arduinoReceiver);\r\n\t}\r\n\r\n\t\/\/ BroadcastReceiver from Amarinos Sensor Graph example\r\n\tpublic class ArduinoReceiver extends BroadcastReceiver {\r\n\t\t@Override\r\n\t\tpublic void onReceive(Context context, Intent intent) {\r\n\t\t\tString data = null;\r\n\r\n\t\t\tfinal int dataType = intent.getIntExtra(AmarinoIntent.EXTRA_DATA_TYPE, -1);\r\n\r\n\t\t\tif (dataType == AmarinoIntent.STRING_EXTRA){\r\n\t\t\t\tdata = intent.getStringExtra(AmarinoIntent.EXTRA_DATA);\r\n\r\n\t\t\t\tif (data != null) {\r\n\t\t\t\t\tString[] parts = data.split(\":\");\r\n\t\t\t\t\tif (parts[0] == \"ERR\") {\r\n\t\t\t\t\t\tToast toast = Toast.makeText(context, parts[1], Toast.LENGTH_LONG);\r\n\t\t\t\t\t\ttoast.show();\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (parts[0].equals(\"GPF\")) {\r\n\t\t\t\t\t    \/\/ sprintf(message,\"GPF:%d:%d:%s:%c:%s:%c:%s:%s:%d\", GPS.fix,GPS.fixquality,latitude,GPS.lat,longitude,GPS.lon,speed,angle,GPS.satellites);\r\n\r\n\t\t\t\t\t\tfloat lat = Float.parseFloat(parts[3]);\r\n\t\t\t\t\t\tlat \/= 100;\r\n\t\t\t\t\t\tfloat lon = Float.parseFloat(parts[5]);\r\n\t\t\t\t\t\tlon \/= 100;\r\n\t\t\t\t\t\tToast toast = Toast.makeText(context, String.format(\"%.6f; %.6f\", lat,lon), Toast.LENGTH_LONG);\r\n\t\t\t\t\t\ttoast.show();\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse {\r\n\t\t\t\t\t\tToast toast = Toast.makeText(context, data, Toast.LENGTH_SHORT);\r\n\t\t\t\t\t\ttoast.show();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\/\/ example on sending data from another class\r\nAmarino.sendDataToArduino(_context, APControlActivity.btBoat, 'a', String.format(\"SPD:%d\", level));<\/pre>\r\nArduino code on the bridge (using <a href=\"http:\/\/www.amarino-toolkit.net\/\">Amarino<\/a> and <a href=\"https:\/\/github.com\/karlp\/Mrf24j40-arduino-library\">Mrf24j40-arduinio-library<\/a>)\r\n<pre lang=\"c\">#include <SPI.h>\r\n#include <mrf24j.h>\r\n#include <MeetAndroid.h>\r\n\r\n\/\/ pins needed by the mrf module\r\nconst int pin_reset = 6;\r\nconst int pin_cs = 7;\r\nconst int pin_interrupt = 2;\r\n\r\n\/\/ settings for the ZigBee network\r\nconst int wifi_address_controller = 0x4201;\r\nconst int wifi_address_boat = 0x4202;\r\nconst int wifi_pan = 0x47CC;\r\n\r\nMrf24j mrf(pin_reset, pin_cs, pin_interrupt);\r\n\r\n\/\/ bluetooth\r\nMeetAndroid meetAndroid;\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n\r\n  \/\/ initialize the mrf module\r\n  mrf.reset();\r\n  mrf.init();\r\n\r\n  mrf.set_pan(wifi_pan);\r\n  mrf.address16_write(wifi_address_controller); \r\n\r\n  \/\/ attach interrupt handler for mrf module\r\n  attachInterrupt(0, interrupt_routine, CHANGE);\r\n  last_time = millis();\r\n  interrupts();\r\n\r\n  \/\/ register handler for data from bluetooth\r\n  meetAndroid.registerFunction(btReceived, 'a');  \r\n}\r\n\r\n\/\/ mrf interrupt handler\r\nvoid interrupt_routine() {\r\n    mrf.interrupt_handler();\r\n}\r\n\r\nvoid loop() {\r\n    mrf.check_flags(&amp;handle_rx, &amp;handle_tx);\r\n    meetAndroid.receive();\r\n    delay(100);\r\n}\r\n\r\n\/\/ mrf receive handler\r\nvoid handle_rx() {\r\n    char data[mrf.rx_datalength()];\r\n    for (int i = 0; i &lt; mrf.rx_datalength(); i++) {         data[i] = mrf.get_rxinfo()-&gt;rx_data[i];\r\n    }\r\n    \/\/ just forward over bluetooth\r\n    meetAndroid.send(data);\r\n}\r\n\r\n\/\/ mrf transfer handler\r\nvoid handle_tx() {\r\n    if (mrf.get_txinfo()-&gt;tx_ok) {\r\n    } else {\r\n        \/\/ send back error message\r\n        char msg[64];\r\n        sprintf(msg, \"ERR:TX failed after %d retries\", mrf.get_txinfo()-&gt;retries);\r\n        meetAndroid.send(msg);\r\n    }\r\n}\r\n\r\n\/\/ --- bluetooth ---\r\n\/\/ bluetooth handler\r\nvoid btReceived(byte flag, byte numOfValues)\r\n{\r\n  \/\/ just forward over mrf\r\n  int length = meetAndroid.stringLength();\r\n  char data[length];\r\n  meetAndroid.getString(data);\r\n  mrf.send16(wifi_address_boat, data);\r\n}<\/pre>\r\nArduino code on the boat (not cleaned up yet, using the mrf24j40-arduino-library and the <a href=\"http:\/\/learn.adafruit.com\/adafruit-ultimate-gps\/parsed-data-output\">Adafruit GPS library<\/a>)\r\n<pre lang=\"c\">#include <SPI.h>\r\n#include <mrf24j.h>\r\n#include <Adafruit_GPS.h>\r\n\/*#if ARDUINO &gt;= 100\r\n #include \r\n#else\r\n  \/\/ Older Arduino IDE requires NewSoftSerial, download from:\r\n  \/\/ http:\/\/arduiniana.org\/libraries\/newsoftserial\/\r\n #include \r\n \/\/ DO NOT install NewSoftSerial if using Arduino 1.0 or later!\r\n#endif*\/\r\n\r\n\/\/ boat\r\nint motorL = 5;\r\nint motorR = 6;\r\nint minValue = 70;\r\nint maxValue = 230;\r\nint incomingByte = 0;   \/\/ for incoming serial data\r\nint currentByte = 0;\r\nint readValue = 0;\r\n\r\nint speed = 0;\r\nfloat steering = 0;\r\n\r\n\/\/ wireless\r\nconst int pin_reset = 6;\r\nconst int pin_cs = 7; \/\/ default CS pin on ATmega8\/168\/328\r\nconst int pin_interrupt = 2; \/\/ default interrupt pin on ATmega8\/168\/328\r\nconst int wifi_address_controller = 0x4201;\r\nconst int wifi_address_boat = 0x4202;\r\nconst int wifi_pan = 0x47CC;\r\n\r\nlong last_time;\r\nlong tx_interval = 1000;\r\n\r\nMrf24j mrf(pin_reset, pin_cs, pin_interrupt);\r\n\r\n\/\/ GPS\r\n\/*#if ARDUINO &gt;= 100\r\n  SoftwareSerial mySerial(3, 4);\r\n#else\r\n  NewSoftSerial mySerial(3, 4);\r\n#endif*\/\r\nAdafruit_GPS GPS(&amp;Serial1);\r\n\r\nboolean usingInterrupt = false;\r\nvoid useInterrupt(boolean); \/\/ Func prototype keeps Arduino 0023 happy\r\n\r\nuint32_t timer = millis();\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n\r\n  \/\/ initialize mrf module\r\n  mrf.reset();\r\n  mrf.init();\r\n\r\n  mrf.set_pan(wifi_pan);\r\n  mrf.address16_write(wifi_address_boat); \r\n\r\n  attachInterrupt(0, wifi_handle_interrupt, CHANGE);\r\n  last_time = millis();\r\n  interrupts();\r\n\r\n  \/\/ initialize GPS module\r\n  GPS.begin(9600);\r\n  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);\r\n  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);\r\n  useInterrupt(true);\r\n}\r\n\r\nvoid loop() {\r\n    mrf.check_flags(&amp;handle_rx, &amp;handle_tx);\r\n    unsigned long current_time = millis();\r\n    if (current_time - last_time &gt; tx_interval) {\r\n        last_time = current_time;\r\n    }\r\n\r\n  if (GPS.newNMEAreceived()) {\r\n    if (!GPS.parse(GPS.lastNMEA()))   \/\/ this also sets the newNMEAreceived() flag to false\r\n      return;  \/\/ we can fail to parse a sentence in which case we should just wait for another\r\n  }\r\n  if (timer &gt; millis())  timer = millis();\r\n\r\n  if (millis() - timer &gt; 2000) { \r\n    timer = millis(); \/\/ reset the timer\r\n\r\n    char message[64];\r\n    if (GPS.fix) {\r\n      \/\/ workaround for sprintf(\"%f\"); bug\r\n      char latitude[10];\r\n      dtostrf(GPS.latitude,6,4, latitude);\r\n      char longitude[10];\r\n      dtostrf(GPS.longitude,6,4, longitude);\r\n      char speed[6];\r\n      dtostrf(GPS.speed,4,2, speed);\r\n      char angle[7];\r\n      dtostrf(GPS.angle,4,2, angle);\r\n      sprintf(message,\"GPF:%d:%d:%s:%c:%s:%c:%s:%s:%d\", GPS.fix,GPS.fixquality,latitude,GPS.lat,longitude,GPS.lon,speed,angle,GPS.satellites);\r\n    }\r\n    else {\r\n      sprintf(message,\"GPS:%d:%d:%d\", (int)GPS.fix,(int)GPS.fixquality,GPS.satellites);\r\n    }\r\n    Serial.println(message);\r\n    mrf.send16(wifi_address_controller, message);\r\n  }\r\n\r\n}\r\n\r\nvoid setSpeed(byte flag, byte numOfValues) {\r\n  speed = minValue + (((maxValue - minValue)\/ 100) * 0); \/\/meetAndroid.getInt());\r\n  setMotors();\r\n}\r\n\r\nvoid setSteering(byte flag, byte numOfValues) {\r\n  steering = 0; \/\/meetAndroid.getFloat();\r\n  setMotors();\r\n}\r\n\r\nint steeringMultiplyer = 10;\r\n\r\nvoid setMotors() {\r\n  int lSpeed = speed;\r\n  int rSpeed = speed;\r\n  if (steering &lt; 0) {     rSpeed -= (steering * steeringMultiplyer);   }   if (steering &gt; 0) {\r\n    lSpeed += (steering * steeringMultiplyer);\r\n  }\r\n  if (lSpeed &gt; maxValue) {\r\n    lSpeed = maxValue;\r\n  }\r\n  if (rSpeed &gt; maxValue) {\r\n    rSpeed = maxValue;\r\n  }\r\n  analogWrite(motorL, lSpeed);\r\n  analogWrite(motorR, rSpeed);\r\n}\r\n\r\n\/\/ --- Wireless ---\r\n\r\nvoid wifi_handle_interrupt() {\r\n    mrf.interrupt_handler(); \/\/ mrf24 object interrupt routine\r\n}\r\n\r\nvoid handle_rx() {\r\n    Serial.print(\"received a packet \");Serial.print(mrf.get_rxinfo()-&gt;frame_length, DEC);Serial.println(\" bytes long\");\r\n\r\n    if(mrf.get_bufferPHY()){\r\n      Serial.println(\"Packet data (PHY Payload):\");\r\n      for (int i = 0; i &lt; mrf.get_rxinfo()-&gt;frame_length; i++) {\r\n          Serial.print(mrf.get_rxbuf()[i]);\r\n      }\r\n    }\r\n\r\n    Serial.println(\"\\r\\nASCII data (relevant data):\");\r\n    for (int i = 0; i &lt; mrf.rx_datalength(); i++) {         Serial.write(mrf.get_rxinfo()-&gt;rx_data[i]);\r\n    }\r\n\r\n    Serial.print(\"\\r\\nLQI\/RSSI=\");\r\n    Serial.print(mrf.get_rxinfo()-&gt;lqi, DEC);\r\n    Serial.print(\"\/\");\r\n    Serial.println(mrf.get_rxinfo()-&gt;rssi, DEC);\r\n}\r\n\r\nvoid handle_tx() {\r\n    if (mrf.get_txinfo()-&gt;tx_ok) {\r\n        Serial.println(\"TX went ok, got ack\");\r\n    } else {\r\n        Serial.print(\"TX failed after \");Serial.print(mrf.get_txinfo()-&gt;retries);Serial.println(\" retries\\n\");\r\n    }\r\n}\r\n\r\n\/\/ --- GPS ---\r\n\r\n\/\/ Interrupt is called once a millisecond, looks for any new GPS data, and stores it\r\nSIGNAL(TIMER0_COMPA_vect) {\r\n  char c = GPS.read();\r\n  \/\/ if you want to debug, this is a good time to do it!\r\n}\r\n\r\nvoid useInterrupt(boolean v) {\r\n  if (v) {\r\n    \/\/ Timer0 is already used for millis() - we'll just interrupt somewhere\r\n    \/\/ in the middle and call the \"Compare A\" function above\r\n    OCR0A = 0xAF;\r\n    TIMSK0 |= _BV(OCIE0A);\r\n    usingInterrupt = true;\r\n  } else {\r\n    \/\/ do not call the interrupt function COMPA anymore\r\n    TIMSK0 &amp;= ~_BV(OCIE0A);\r\n    usingInterrupt = false;\r\n  }\r\n}<\/pre>","type":"rich","thumbnail_url":"https:\/\/projects.schneidr.de\/files\/2012\/10\/P1150231-150x150.jpg","thumbnail_width":150,"thumbnail_height":150}