ADH8066 SMS termination issues.

Hi there,

I have been playing with an AD8066 (and Sparkfun’s associated breakout) connected to a computer, using C# and the .net serialPort class for control.

I have no issues with receiving messages through this, however the escape sequence for sending sms messages does not seem to work. AT commands are fine, and the code I was using to send was working on a nokia phone not long ago.

Using text mode (and the instructions in http://www.sparkfun.com/datasheets/Cell … -guide.pdf) I don’t seem to be able to send ctrl + z correctly. Have tried sending (char)26, and a bunch of other tricks with no success.

This is the message sending code:

		//Function to send sms messages with raw input.
		public bool SendMessage(string number, string message) {
			bool interruptDisabled = false;

			//If serial received interrupt is enabled, disable it for the duration of the action.
			if (interrupt) {
				DetachInterrupt();
				interruptDisabled = true;
			}

			//Set text mode.
			Write("AT+CMGF=1\r\n");

			Thread.Sleep(10);

			//Set destination phone number and open text editing. The phone does not ack this.
			Write("AT+CMGS=\"" + number + "\"" + "\r\n");

			Thread.Sleep(10);

			//Write Message
			Write(message);

			Thread.Sleep(10);

            //Terminating characters.
            //Currently fixing this.
			//Write("\x1A\x1A\r"); Attempt using hex.
                        //Write("\u001F\u001F"); Unicode equivalent character.
            if(serialPort.IsOpen) {
                byte[] terminatingChars = new byte[2];
                terminatingChars[0] = 26;
                terminatingChars[1] = 26;

                serialPort.Write(terminatingChars, 0, 2);
            }
            Write("AT");

			Thread.Sleep(10);

			//Reenable interrupt if it has been disabled
			if (interruptDisabled) {
				AttachInterrupt();
			}

			//Flush any serial data that may have been recieved.
			ReadSerial();

			return true;
		}

Also using my serial write method:

//Function to safely write to serial port.
		public bool Write(string message) {
			//Check that serial port is open.
			if (serialPort.IsOpen) {
				//Try to write message out serialPort.
				try {
					serialPort.Write(message);
                                //Todo: catch exceptions separately.
				} catch (Exception) {
					Log.WriteLine("Serial Output Exception");
					return false;
				}
				return true;
			}
			return false;
		}

And this is the whole class, that should compile in .net or Mono:

//Nokia Phone Serial Command Interface Class.
//Defines a phone class with control and interrupts.

//ENSURE INTERRUPT IS ATTACHED ONLY AFTER CONFIGURATION.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace SMSInterface {
	public class Phone {
		public SerialPort serialPort = new SerialPort();
		private bool interrupt;
		private string messageCenter;
		private Queue<Message> outgoingMessages = new Queue<Message>();
		private Queue<Message> incomingMessages = new Queue<Message>();

		public Phone() {
			//TODO Set configurations and stuff.
			//lolkay
		}

		//Deconstructor to ensure serial port is properly closed.
		~Phone() {
			Stop();
		}

		public bool Start(string portIn) {
			//Set port.
			if (!SetPort(portIn)) {
				return false;
			}

			//Initialise Hardware
			Log.WriteLine("Initialising Hardware.");
			ConfigureSerial();
			OpenSerial();

			//Handshaking to ensure phone communicating.
			if (IsAvailable()) {
				ConfigurePhone();

				//Attach Serial Event Interrupt.
				AttachInterrupt();

				//Inform the user.
				Log.WriteLine("Hardware Initialised.");
				return true;
			} else {
				return false;
			}
		}

		public bool Stop() {
			try {
				DetachInterrupt();
				serialPort.Close();
				serialPort.Dispose();
				return true;
			} catch (Exception e)   //Todo: this should only catch serial exceptions.
			{
				return false;
			}
		}

		//Opens associated serial port.
		private bool OpenSerial() {
			//Open Serial Port
			Log.WriteLine("Opening Serial Port");

			try {
				serialPort.Open();
			} catch (Exception e) {
				//TODO make not suck.
				//i.e handle the different possible exceptions this can throw
				//individually
				Log.WriteLine("Serial Port Exception");
				Log.WriteLine(e.Message);
				return false;
			}

			if (serialPort.IsOpen) {
				Log.WriteLine("Serial Port Open");
				Thread.Sleep(500);
				return true;
			}

			return false;
		}

		//Method to check phone is available.
		public bool IsAvailable() {
			//At should return OK.
			if (SendCommand("AT")) {
				return true;
			}
			return false;
		}

		//Interface method to check if messages are available.
		public bool MessagesAvailable() {
			if (incomingMessages.Count() > 0) {
				return true;
			} else {
				return false;
			}
		}

		//Interface method to retrieve message from buffer.
		public Message GetMessage() {
			return incomingMessages.Dequeue();
		}

		//Function to send sms messages with raw input.
		public bool SendMessage(string number, string message) {
			bool interruptDisabled = false;

			//If interrupt is enabled, disable it for the duration of the action.
			if (interrupt) {
				DetachInterrupt();
				interruptDisabled = true;
			}

			//Set text mode.
			Write("AT+CMGF=1\r\n");

			Thread.Sleep(10);

			//Set destination phone number and open text editing. The phone does not ack this.
			Write("AT+CMGS=\"" + number + "\"" + "\r\n");

			Thread.Sleep(10);

			//Write Message
			Write(message);

			Thread.Sleep(10);

            //Terminating characters.
            //Currently fixing this.
			//Write("\x1A\x1A\r");
            Write("\u001F\u001F");
            if(serialPort.IsOpen) {
                byte[] terminatingChars = new byte[2];
                terminatingChars[0] = 26;
                terminatingChars[1] = 26;

                //serialPort.Write(terminatingChars, 0, 2);
            }
            Write("AT");

			Thread.Sleep(10);

			//Reenable interrupt if it has been disabled
			if (interruptDisabled) {
				AttachInterrupt();
			}

			//Flush any serial data that may have been recieved.
			ReadSerial();

			return true;
		}

		//Overloaded function to send sms messages with message class.
		public bool SendMessage(Message message) {
			return SendMessage(message.number, message.content);
		}

		//Function to safely (handled) write to serial port.
		public bool Write(string message) {
			//Check that serial port is open.
			if (serialPort.IsOpen) {
				//Try to write message out serialPort.
				try {
					serialPort.Write(message);
				} catch (Exception) {
					Log.WriteLine("Serial Output Exception");
					return false;
				}
				return true;
			}
			return false;
		}

		//Function to send and confirm AT commands.
		private bool SendCommand(string command) {
			int count = 0;
			string incoming;
			bool interruptDisabled = false;

			//Check serial port is open.
			if (!serialPort.IsOpen) {
				Log.WriteLine("Serial not available");
				return false;
			}

			//If interrupt is enabled, disable it for the duration of the action.
			if (interrupt) {
				DetachInterrupt();
				interruptDisabled = true;
			}

			//Send Command to phone.
			Write(command + "\r\n");

			// Loop until data recieved or timeout occurs.
			while (true) {
				if (serialPort.BytesToRead > 0) {
					//Give the phone some time to finish responding
					Thread.Sleep(10);
					//Try to read incoming data.
					try {
						incoming = serialPort.ReadLine();
					} catch (Exception e) {
						Log.WriteLine(e.ToString());
						return false;
					}

					//Parse incoming data.
					if (incoming.Contains("OK")) {
						//If command was successful, 
						//Reenable interrupt if it has been disabled
						if (interruptDisabled) {
							AttachInterrupt();
						}

						//inform calling function.
						return true;
					} else if (incoming.Contains("ERROR")) {
						//Try Again.
						Write(command + "\r");
					}
				}

				//Count and escape after 10 attempts.
				count++;
				if (count > 10) {
					Log.WriteLine("Serial Command Fail: " + command);
					//Reenable interrupt if it has been disabled
					if (interruptDisabled) {
						AttachInterrupt();
					}
					return false;
				}
				//Delay to let the dust settle.
				Thread.Sleep(10);
			}
		}

		//Attach or unattach interrupt listener.
		private void AttachInterrupt() {
			interrupt = true;
			serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.SerialPort_DataRecieved);
		}
		private void DetachInterrupt() {
			serialPort.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(this.SerialPort_DataRecieved);
			interrupt = false;
		}

		//Set serial port.
		private bool SetPort(string input) {
			string[] ports;

			//Get available port names.
			ports = SerialPort.GetPortNames();

			//If the port is valid.
			if (ports.Contains(input)) {
				//Set this as the port for use.
				serialPort.PortName = input;

				return true;
			} else {
				Log.WriteLine("Invalid Serial Port: " + input);
				return false;
			}
		}

		//Configuration data for the serial port.
		private void ConfigureSerial(int baudRate, int timeout) {
			serialPort.BaudRate = baudRate;
			serialPort.WriteTimeout = timeout;
		}

		//Overload for default values
		private void ConfigureSerial() {
			ConfigureSerial(115200, 1000);
		}

		//Overload to take a single input value
		private void ConfigureSerial(int value, bool valueIsTimeout) {
			if (valueIsTimeout) {
				ConfigureSerial(115200, value);
			} else {
				ConfigureSerial(value, 1000);
			}
		}

		//Configure Phone.
		private bool ConfigurePhone() {
			//Set text mode.
			SendCommand("AT+CMGF=1");
			//Set Message Reciept Mode.
			SendCommand("AT+CNMI=1,2,0,0,0");

			return true;
		}

		public void ReadSerial() {
			string incoming;
			string content;
			string existing;
			while (serialPort.BytesToRead > 0) {
				//Give it a little time to recieve the message.
				Thread.Sleep(10);
				//Note that this blocks until a line is recieved.
				incoming = serialPort.ReadLine();
				Console.WriteLine(incoming);

				//Interpret incoming messages.
				if (incoming.StartsWith("+CMT")) {
					//Mesage incoming.
					Log.WriteLine("Message Incoming");
					content = serialPort.ReadLine();

					//Add message to queue.
					Message temp = new Message(incoming, content);
					incomingMessages.Enqueue(temp);

					break;

				} else if (incoming.StartsWith("RING")) {
					//Someone is calling.
					Log.WriteLine("RING");
				} else if (incoming == "\r") {
					//Ignore blank newlines.
				} else {
					//Unrecognised.
					Log.WriteLine("> " + incoming);
				}
			}
		}

		//Data recieved interrupt for the serial port.
		private void SerialPort_DataRecieved(object sender, SerialDataReceivedEventArgs e) {
			ReadSerial();
		}
	}
}

Any help would be appreciated :slight_smile:

You need to terminate the text portion of the text message with the ascii charactor 0x1A instead of a carrage return. The data sheet calls this charactor ctrl-z.