Bi-directional satellite communication and messaging!
You know that moment in the Kingsman movie when Merlin tells Eggsy hes looking good and in reply, Eggsy replies “Feeling good, Merlin?” I totally feel that way not because of my clothes, but because of my success in integrating the Rockseven Rockblock Naked modem into LOCARBs electronics suite!
Why did it take so long?
Since my last little update (which was a month ago), I was letting my mind wander from LOCARB programming, and waiting for the logic level shifters and KST servos to come in the mail. About a week after the update, I received the servos and was able to give them a quick test. Everything looked great and despite being high voltage servos, they still ran undervolted at 5.5v (with a 1.69 amp stall current – at 8v, the servos had ~3amps stall current) which gave me some flexibility as to how to power them depending on my desired power requirements.
I then put the servos to the side, and waited for the logic level shifters to arrive…and waited…and waited, and waited. It might have been my fault for buying in bulk from China for the amount less than getting them from Adafruit or Sparkfun, but most things I buy off Ebay only take about 2 weeks to arrive. Anyways, about 3 days ago, I got tired of waiting and hooked up my 74AHCT125 – Quad Level-Shifter I had from an old Neopixel project and got to work (and yes, after I hooked up the 74AHCT125, the Ebay shifters came in the mail the next day, sigh…).
The Process
During the time I was waiting for the logic level shifters to come in, I was doing a little thinking about how to handle the data being sent to and from LOCARB. I realized I needed to create my own coded message which would need to be formatted in a way which could be consistently parsed by LOCARB into usable commands and data. For sending, I just created a comma separated string of all the variables, which I could then receive and sort out later. But for receiving I didn’t really know how to go about parsing the data (I didn’t even know what the data would look like after being received) so I was a little intimidated. I also needed to think about what circumstances would necessitate me to send messages to LOCARB; which ends up being only in the event I need to recover from malfunctions, handle a low power event, or a issue a course correction.
This is what I came up with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
COMMANDS: 1. MOTOR OFF/ON (0 - DO NOTHING, 1 ON, 2+ OFF) 2. RESTART PIXHAWK VIA MAVLINK after 15 seconds (0 NO, 1+ YES) 3. POWER CYCLE PX4 & SERVO (0 - NO, 1 - YES, 2 - DISABLE PIXHAWK HB WATCHDOG, 3 - ENABLE PIXHAWK HB WATCHDOG) 4. RESTART ARDUINO (0 - NO, 1 - YES, 2 - DISABLE ARDUINO WATCHDOG, 3 - ENABLE ARDUINO WATCHDOG) 5. CHANGE SEND/RECEIVE INTERVAL (0 NO, 1-6 set freq) 6. NEW WAYPOINT 0 = NO, 1+ = YES and ENTER IN 7 (LAT) and 8 (LONG) 7. LAT 8. LONG FORMAT: Need to specify 0 as placeholders if changing any one variable. Multiple variables can be changed at the same time, but placeholder 0's must be used. 1 2 3 4 5 6 7 8 0|0|0|0|0|0|,LAT|,:LONG| If NEW WAYPOINT FLAG = 0 then it is OK to just send commands only 0|0|0|0|6|0| Example Message: 000201,23.0529340,:-141.070312 RESULT: -Disable Arduino Watchdog completely -Tell Locarb it has a new GPS Waypoint -Update Pixhawk with new Latitude and Longitude |
To further elaborate, the first 6 bytes of the message contain the commands, with each byte being able to control multiple states. The message is formatted without any delimiters except in the event GPS coordinates are involved. This is because of the tokenizing which is required to split a string apart for processing. I could have just used one byte to control all the states, but breaking them up into more bytes allows multiple states to be controlled in one message and helped to simplify debugging.
The processing code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
if (send_successful) { //Process ROCKBLOCK MESSAGES Serial.println("\nSend successful, now processing inbound command message:"); //Received values are decimal integers representing ASCII characters. Needs to be converted to Strings //or math done to obtain correct ASCII numerical value. ig., 48 = 0 in ASCII //Process FIRST BYTE of array - MOTOR MONITORING FUNCTIONS if(inBuffer[0] - 48 == 1) //Set monitor on and start motor { motor_Switch = 1; initialize_Motor(); //Initialize Motor Serial.println("Motor Initialized"); } else { if (inBuffer[0] - 48 >= 2) //Set Monitor to off and stop motor { motor_Switch = 0; //turn off motor monitor function disable_Motor(); //Disable Motor Serial.println("Motor Disabled "); } } //Process SECOND BYTE of array - SOFT REBOOT PIXHAWK if (inBuffer[1] - 48 >= 1) { pixhawk_reboot_Switch = 1; //Request Reboot from Pixhawk previous_reboot_Millis = millis(); Serial.println("Rebooting Pixhawk in 15 seconds"); } //Process THIRD BYTE of array - PIXHAWK AND SERVO FUNCTIONS if (inBuffer[2] - 48 == 1) //POWER CYCLE PIXHAWK AND SERVO { previous_pixhawk_servo_Millis = millis(); pixhawk_servo_Switch = 1; digitalWrite(pxreset_Pin, LOW); //Turn off power to pixhawk Serial.println("Pixhawk and Servo Power Cycled"); } if (inBuffer[2] - 48 == 2) //DISABLE PIXHAWK HB WATCHDOG { //TURN OFF PIXHAWK HB Watchdog Timer pixhawk_HBsatswitch = 0; Serial.println("\nPIXHAWK HB Watchdog Timer DISABLED"); } if (inBuffer[2] - 48 == 3) //DISABLE PIXHAWK HB WATCHDOG { //TURN ON PIXHAWK HB Watchdog Timer pixhawk_HBsatswitch = 1; Serial.println("\nPIXHAWK HB Watchdog Timer ENABLED"); } //Process FOURTH BYTE of array ARDUINO WATCHDOG TIMER FUNCTIONS if (inBuffer[3] - 48 == 1) //REBOOT ARDUINO { arduino_Watchdog_switch = 0; //Disable Arduino Watchdog, will reset Arduino within 6:30 Serial.println("Arduino Watchdog Turned off, Rebooting in 6~7 minutes..."); } if (inBuffer[3] - 48 == 2) //DISABLE ARDUINO WATCHDOG TIMER { //TURN OFF Watchdog Timer Arduino_WDsatswitch = 0; digitalWrite(watchdog_Pin, HIGH); Serial.println("\nArduino Watchdog Timer DISABLED"); } if (inBuffer[3] - 48 == 3) //ENABLE ARDUINO WATCHDOG TIMER { //TURN ON Watchdog Timer Arduino_WDsatswitch = 1; digitalWrite(watchdog_Pin, LOW); Serial.println("\nArduino Watchdog Timer ENABLED"); } //Process FIFTH BYTE of array - SET DATA COLLECTION AND MESSAGING FREQUENCY if (inBuffer[4] - 48 >= 1 && inBuffer[4] - 48 <= 6) { datcol_Frequency = (inBuffer[4] - 48); datcol_Time = (inBuffer[4] - 48); Serial.print("\nData collection interval set to: "); Serial.print(datcol_Frequency);Serial.print(" (Millis: "); Serial.print(datcol_Interval[datcol_Frequency]); Serial.println(")"); } //Process SIXTH BYTE of array - PROCESS NEW GPS WAYPOINT AND UPLOAD TO PIXHAWK if (inBuffer[5] - 48 >= 1) { //PROCESS NEW GPS WAYPOINT Serial.println("\nReceived NEW GPS Waypoint"); //Process and tokenize GPS Latitude String actual = strtok(inBuffer,","); String latitude = strtok(NULL, ","); Serial.print("Latitude: ");Serial.println(latitude); latitude_Float = latitude.toFloat(); Serial.print("FLOAT Latitude: ");Serial.println(latitude_Float,7); //Process and tokenize GPS Longitude String longitude = strtok(NULL, ":"); Serial.print("\nLongitude: ");Serial.println(longitude); longitude_Float = longitude.toFloat(); Serial.print("FLOAT Longitude: ");Serial.println(longitude_Float,6); //GPS WAYPOINT ERROR CHECK: - Even if new gps coords flag set, only update if both GPS coordinates are not empty if (longitude_Float != 0 && latitude_Float !=0) { mavlink_Wpreceive_switch = 1; //enable receive of pixhawk messages for mission protocol mission_count(); //begin mission protocol Serial.println("\nNew GPS Waypoint sent to Pixhawk"); } } } |
The only time I do not access an address of the buffer manually is when there are GPS coordinates to tokenize. I knew nothing of tokenizing prior to this stage in programming, so i’m not even sure I have the correct usage implemented. All I know is that it works reliably.
The Latitude strtok function searches for a “,” and sets a pointer there. Then it will continue until it finds another “,” and tokenizes the data between the two as a string. I then convert the string to a float and store it in a variable to be uploaded to the Pixhawk via a Mavlink message. The same is true of the Longitude strtok function but using a “:”, since the strtok function finishes when it hits a null character, I don’t need to specify an end character.
In my testing, I realized that there could be a stray new GPS waypoint flag sent in event of a typo or other error, so I added a simple error check to make sure both Latitude and Longitude are present before a waypoint update is sent to the Pixhawk.
Some other considerations
There are some other considerations related to this entire deal with satellite communications, I’ve listed them below.
Timing of the first message sent:
- I’m using a standalone watchdog timer from freetronics which by design, requires a HIGH signal sent to it before 5 minutes, otherwise it will send a reset signal to the Arduino. I have this in case the Arduino locks up and needs to be reset. By design, the little watchdog timer is supposed to be reset before every 5 minute counter, but after some testing I realized that it is not so consistent. On first reboot without sending a HIGH, it will wait approximately 6:20 seconds before it sends its reset signal. After that initial 6:20, it then will only wait 3:20 seconds before sending another reset signal.In the event the watchdog has an error and constantly resets the Arduino, i’ve added the ability to send a constant HIGH to the watchdog and disable it completely (using a sat message). I also tweaked my sat comm messaging to use an initial boot send/receive of 1 minute, then switch to any other desired frequency value afterwards. This is to allow for obtaining a command from a satellite message prior to the watchdog resetting the Arduino. I’m considering taking it a little further and implementing a MOSFET to completely disconnect the watchdog from power.
Timing and other functions:
- The Rockblock modem seems to take 4 minutes before it times out in the event a send/receive failure occurs. This can happen because there isn’t always a satellite flying above the modem all the time or there’s too much interference (signals, line of sight, etc). Testing shows signal quality rising and waning as the satellites pass overhead. The IridumSBD library is blocking code which means that when the Arduino is running the send/receive function, it will prohibit any other code from running. This isn’t a major problem for my application as the navigation is handled by another independent controller, but it could cause some trouble with any other function which needs to be run/updated. Because of my watchdog timer, and the likely event several failed attempts at communication would block the code longer than the HIGH signal is sent to reset the watchdog, I need to make sure I send a constant HIGH to the watchdog until the Sat modem send/receive function is completed, and return the watchdog to its normal state afterwards.