Cannot send SMS more then 140 via HUAWEI USB stick modem - c#

I use HUAWEI USB stick modem and code below to send SMS successfully but under 140 of length (see the code pls -- double lenMes = textsms.Length / 2;).
But nowdays I see the really big SMS messages.
So I am wondering what's wrong with AT commnds or may me hardware is old so I cannot send big SMS.
Please any clue?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
namespace sendSMSPDU
{
class Program
{
static SerialPort port;
static void Main(string[] args)
{
port = new SerialPort();
Console.WriteLine("Sending SMS");
OpenPort();
bool result;
result = sendSMS("Some text that less 140 is gonna sending OK", " +75434355544");
if (result == true)
{
Console.WriteLine("OK");
}
else
{
Console.WriteLine("ERROR");
}
Console.ReadLine();
port.Close();
}
private static bool sendSMS(string textsms, string telnumber)
{
if (!port.IsOpen) return false;
try
{
System.Threading.Thread.Sleep(500);
port.WriteLine("AT\r\n");
System.Threading.Thread.Sleep(500);
port.Write("AT+CMGF=0\r\n");
System.Threading.Thread.Sleep(500);
}
catch
{
return false;
}
try
{
telnumber = telnumber.Replace("-", "").Replace(" ", "").Replace("+", "");
telnumber = "01" + "00" + telnumber.Length.ToString("X2") + "91" + EncodePhoneNumber(telnumber);
textsms = StringToUCS2(textsms);
string leninByte = (textsms.Length / 2).ToString("X2");
textsms = telnumber + "00" + "08" + leninByte + textsms;
double lenMes = textsms.Length / 2;
if (lenMes < 140) // It sends OK
{
port.Write("AT+CMGS=" + (Math.Ceiling(lenMes)).ToString() + "\r\n");
System.Threading.Thread.Sleep(500);
textsms = "00" + textsms;
port.Write(textsms + char.ConvertFromUtf32(26) + "\r\n");
System.Threading.Thread.Sleep(500);
}
else
{
return false;
}
}
catch
{
return false;
}
try
{
string recievedData;
recievedData = port.ReadExisting();
if (recievedData.Contains("ERROR"))
{
return false;
}
}
catch { }
return true;
}
private static void OpenPort()
{
port.BaudRate = 9600;
port.DataBits = 7;
port.StopBits = StopBits.One;
port.Parity = Parity.Odd;
port.ReadTimeout = 500;
port.WriteTimeout = 500;
//port.Handshake = Handshake.RequestToSend;
//port.DtrEnable = true;
//port.RtsEnable = true;
//port.NewLine = Environment.NewLine;
port.Encoding = Encoding.GetEncoding("windows-1252");
port.PortName = "COM7";
if (port.IsOpen)
port.Close();
try
{
port.Open();
}
catch { }
}
public static string EncodePhoneNumber(string PhoneNumber)
{
string result = "";
if ((PhoneNumber.Length % 2) > 0) PhoneNumber += "F";
int i = 0;
while (i < PhoneNumber.Length)
{
result += PhoneNumber[i + 1].ToString() + PhoneNumber[i].ToString();
i += 2;
}
return result.Trim();
}
public static string StringToUCS2(string str)
{
UnicodeEncoding ue = new UnicodeEncoding();
byte[] ucs2 = ue.GetBytes(str);
int i = 0;
while (i < ucs2.Length)
{
byte b = ucs2[i + 1];
ucs2[i + 1] = ucs2[i];
ucs2[i] = b;
i += 2;
}
return BitConverter.ToString(ucs2).Replace("-", "");
}
}
}

Single SMS message is limited to 160 (or 152 in PDU mode) symbols in GSM-7 encoding. More than that, if there is any symbol not listed here you need to use UCS-2 encoding and your messages now limit to 67 symbols. If you need to send longer messages, you are welcome to the "bright and shiny world" of SMS PDU mode.
So sending a long sms is as easy as:
Split it to parts of 67 (or 152) symbols;
Convert this parts to UCS-2 or GSM-7 encoding;
Transform them to PDU messages;
Send them sequentially with use of additional AT-command (AT+CMGF=0)
Edit
The one who designed a PDU format is a true evil person. It is really mind breaking thing and I don't want to write a convertion code, sorry. But, I can point you with this stub:
protected void SendMessage(string phoneNumber, string message)
{
const char CR = '\r'; // "Carage Return"
const char CtrlZ = (char)26; // Ctrl+Z character
var header = GeneratePDUHeader(phoneNumber);
foreach (var messagePart in SplitSMSMessage(message))
{
SendToCOM("AT+CMGF=0" + CR);
SendToCOM("AT+CMGS=42" + CR);
SendToCOM($"{header}{messagePart}" + CtrlZ);
}
}
// should return something like "0041000B910000000000F000088C"
protected string GeneratePDUHeader(string phoneNumber) { }
// split long message to parts
protected IEnumerable<string> SplitSMSMessage(string message)
{
var useUCSEncoding = IsUCSEncodingNeeded(message);
var partLength = useUCSEncoding ? 67 : 152;
var messageParts = Enumerable.Range(0, message.Length / partLength)
.Select(i => message.Substring(i * partLength, partLength))
.ToArray();
var referenceNumber = $"{GenerateReferenceNumber():X2}"; // convert to HEX, i.e. "01"
var totalMessagesCount = $"{messageParts.Length:X2}"; // convert to HEX, i.e. "01"
var udhBase = $"050003{referenceNumber}{totalMessagesCount}";
var messageNumber = (char)0;
foreach (var messagePart in messageParts)
{
var udh = $"{udhBase}{++messageNumber}";
var messagePartText = useUCSEncoding ? StringToUCS(messagePart) : StringToGSM7(messagePart);
yield return $"{udh}{messagePartText}";
}
}
private void SendToCOM(string message) { } // writes message to COM port
private bool IsUCSEncodingNeeded(string message) { } // determine if UCS-2 convert is required
private char GenerateReferenceNumber() { } // random number 0-255
private string StringToUCS(string message) { } // convert string to UCS encoding
private string StringToGSM7(string message) { } // convert string to GSM7 encoding (don't forget about padding!)
You may also find this links are useful:
https://en.wikipedia.org/wiki/Concatenated_SMS
https://www.developershome.com/sms/cmgsCommand4.asp

The best answer to my question is here
How to concat long SMS in GSMComm Library?
https://github.com/welly87/GSMComm
https://www.nuget.org/packages/GSMComm/
It work fine with my HUAWEI GSM modem! WOW!

Related

How to read serial port until timeout right after write to it?

I need to write and read data from serial port to my device. I've test certain approach where at first, I'm receiving the data using SerialDataReceivedEventArgs and I feel it is hard to read the port where I need to define the command that send where as the command is almost 200 commands.
My first approach is using:-
private void ObjCom_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (!ObjCom.IsOpen) return;
byte[] data = new byte[ObjCom.BytesToRead];
ObjCom.Read(data, 0, data.Length);
RaiseEventMsg("Buffer String: " + BitConverter.ToString(data).Replace("-", " "));
}
The RaiseEventMsg is a delegate event to pass current information to Main UI. The second approach is:-
private long lngTickCount = Convert.ToInt64(1000L);
public void StartWriteToPort()
{
byte[] Cmd = null;
string strCmd = string.Empty;
string strMsg = null;
bool bCont = true;
long lngCurrent = 0;
long lngNow = 0;
try
{
RaiseEventMsg("Start Write To Port");
ObjCom.DiscardOutBuffer();
ObjCom.DiscardInBuffer();
GetFullCommandByte(ref Cmd, Convert.ToByte(123)); // Referencing Cmd with return and pass Command List(various set of command)
ObjCom.Write(Cmd, 0, Cmd.Length);
strCmd = ByteArrayToString(Cmd); // Convert byte array to Hex string
RaiseEventMsg("Send: " + strCmd);
bool bTimeout = false;
lngCurrent = DateTime.Now.Ticks;
while (!bTimeout)
{
lngNow = DateTime.Now.Ticks;
if (lngNow > (lngCurrent + (3 * lngTickCount)))
{
bTimeout = true;
break;
}
}
lngCurrent = DateTime.Now.Ticks;
while (ObjCom.BytesToRead <= 0)
{
lngNow = DateTime.Now.Ticks;
if (lngNow > (lngCurrent + (1000 * lngTickCount)))
{
bCont = false;
break;
}
}
if (!bCont)
{
strMsg = "Error - Timeout Hit";
RaiseEventMsg(strMsg);
return;
}
int Idx = 0;
string strASCIIFull = string.Empty;
if ((ObjCom.BytesToRead > 0) & (bCont == true))
{
while (ObjCom.BytesToRead > 0)
{
var strASCII = ObjCom.ReadByte();
var TmpHex = System.Convert.ToString(strASCII, 16).ToUpper();
if (TmpHex.Length == 1)
{
strASCIIFull += (" 0" + TmpHex);
}
else
{
strASCIIFull += (" " + TmpHex);
}
lngCurrent = DateTime.Now.Ticks;
while (ObjCom.BytesToRead <= 0)
{
lngNow = DateTime.Now.Ticks;
if (lngNow > (lngCurrent + (2 * lngTickCount)))
{
bCont = false;
break;
}
}
Idx += 1;
}
}
RaiseEventMsg("Recv: " + strASCIIFull);
}
catch (System.Exception ex)
{
string error = $"Exception on StartWriteToPort. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
}
}
Problem on second approach is when I call this function for second time, the timeout will hit . But for Serial event, it does not have the problem, the protocol for timeout is set to 1 seconds. My device currently connected using USB without converter. The input cable to device is type B port (like standard printer port).
Is the any other way to read directly from port or any improvement on current code?
You need to learn how to layer your code. At the moment you have one long function that tries to do everything.
If you had several smaller functions that did specific things like reading or writing a chunk of information then it would make what you are trying to do simpler.
For example, serial communications generally have some sort of protocol that encapsulates how the packets of information are stored. Say the protocol was "", then you know every packet starts with an STX byte (0x01), a length byte, which tells you how many bytes are in the section, and there must be an ETX byte (0x02) at the end. You could write a function that would return an array of bytes that are just the because the function would interpret the stream and extract the relevant parts.
Then it might be as simple as:
var packet1 = ReadPacket();
WritePacket(outputData);
var packet2 = ReadPacket();

How can I replace the string saved on the file

I'm writing a program to find a md5 hash that has a hexadecimal representation with the digits of pi. I got a problem on saving the computations. It looks like the program won't save the string correctly. It makes a long string rather than replacing the old one. How can I fix that problem?
public static string get_last_string()
{
string text;
string text2="";
int i= 0;
string path = "C:\\Users\\uname\\Desktop";
var fileStream = new FileStream(#path + "\\last.txt", FileMode.Open, FileAccess.Read);
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
text = streamReader.ReadToEnd();
}
i=0;
while (i < text.Length && text[i] != ' ')
{
text2 += text[i];
++i;
}
return text2;
}
public static void Main(string[] args)
{
string path = #"C:\Users\Uname\Desktop\test.txt";
string a = get_last_string();
Console.WriteLine("Let us continue from the string " + a);
Console.WriteLine("Ctrl+C stops the computing and saves the results to the file last.txt");
do
{
while (!Console.KeyAvailable)
{
// Console.WriteLine("md5("+a+")="+CalculateMD5Hash(a));
if (CommonPrefix(CalculateMD5Hash(a), "314159265353") >= 11)
{
Console.WriteLine(a + " " + CalculateMD5Hash(a));
File.WriteAllText(path, a + " " + CalculateMD5Hash(a));
}
a = Next(a);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
File.AppendAllText(#path + "\\last.txt", a);
}
You have a File.WriteAllText(... and later on you have File.AppendAllText(...
I'm pretty shure you're writing twice to the file.

Send sms in gsm arduino contact from c#

Good Day, I am currently doing a project wherein i am fetching the number that i will send a message from c#, when i manually input the number it will send but, if it is from the c# it wont. This is my arduino code.:
boolean stringComplete = false;
String inputString = "";
void loop()
{
if(stringComplete)
{
SIM900.print("AT+CMGF=1\r");
delay(100);
SIM900.println("AT + CMGS = \"" +inputString+ "\"");
delay(100);
SIM900.println("This message is from HD Robot. Medicine not enough.");
delay(100);
SIM900.println((char)26);
delay(100);
SIM900.println();
delay(5000);
inputString = "";
stringComplete = false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}
And here is my c# code:
DataTable contact = new DataTable();
contact = database.getContactInformation();
foreach (DataRow rows in contact.Rows)
{
fam = rows["CPNumber"].ToString();
added = "+63" + fam;
serial.Write(added);
}

Serializing a manually written code

I am having a problem receiving files from the client. Someone suggested that I should use binary serialization to send and receive messages in stream. Can you give me ideas on how I should serialize this? I just learned about serialization not long ago so I am quite confused on how I should associate it with my program.
This is the client that 'should' be serialize
public void sendthedata()
{
if (!_timer.Enabled) // If timer is not running send data and start refresh interval
{
SendData();
_timer.Enabled = true;
}
else // Stop timer to prevent further refreshing
{
_timer.Enabled = false;
}
}
private List<int> listedProcesses = new List<int>();
private void SendData()
{
String processID = "";
String processName = "";
String processPath = "";
String processFileName = "";
String processMachinename = "";
listBox1.BeginUpdate();
try
{
piis = GetAllProcessInfos();
for (int i = 0; i < piis.Count; i++)
{
try
{
if (!listedProcesses.Contains(piis[i].Id)) //placed this on a list to avoid redundancy
{
listedProcesses.Add(piis[i].Id);
processID = piis[i].Id.ToString();
processName = piis[i].Name.ToString();
processPath = piis[i].Path.ToString();
processFileName = piis[i].FileName.ToString();
processMachinename = piis[i].Machinename.ToString();
output.Text += "\n\nSENT DATA : \n\t" + processFileName + "\n\t" + processMachinename + "\n\t" + processID + "\n\t" + processName + "\n\t" + processPath + "\n";
}
}
catch (Exception ex)
{
wait.Abort();
output.Text += "Error..... " + ex.StackTrace;
}
NetworkStream ns = tcpclnt.GetStream();
String data = "";
data = "--++" + processFileName + " " + processMachinename + " " + processID + " " + processPath;
if (ns.CanWrite)
{
byte[] bf = new ASCIIEncoding().GetBytes(data);
ns.Write(bf, 0, bf.Length);
ns.Flush();
}
}
}
finally
{
listBox1.EndUpdate();
}
}
And deserializing in the server
private void recieveData()
{
NetworkStream nStream = tcpClient.GetStream();
ASCIIEncoding ascii = null;
while (!stopRecieving)
{
if (nStream.CanRead)
{
byte[] buffer = new byte[1024];
nStream.Read(buffer, 0, buffer.Length);
ascii = new ASCIIEncoding();
recvDt = ascii.GetString(buffer);
/*Received message checks if it has +##+ then the ip is disconnected*/
bool f = false;
f = recvDt.Contains("+##+");
if (f)
{
string d = "+##+";
recvDt = recvDt.TrimStart(d.ToCharArray());
clientDis();
stopRecieving = true;
}
//else if (recvDt.Contains("^^"))
//{
// new Transmit_File().transfer_file(file, ipselected);
//}
/* ++-- shutsdown/restrt/logoff/abort*/
else if (recvDt.Contains("++--"))
{
string d = "++--";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
clientDis();
}
/*--++ Normal msg*/
else if (recvDt.Contains("--++"))
{
string d = "--++";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
}
}
Thread.Sleep(1000);
}
}
public void addToOutput()
{
if (recvDt != null && recvDt != "")
{
output.Text += "\n Received Data : " + recvDt;
recvDt = null;
}
}
Thank you.
There are a couple of rules to follow when serialising a piece of data.
It's easy to convert data to bytes, but consider how to reconstruct the data on the other side. Assume that the server can't have any knowledge on what you sended.
In your serialiser you just convert a couple of strings into a byte[] and send it over. Example:
string x = "abcdef";
string y = "ghijk";
var bytes = Encoding.Ascii.GetBytes(x + y);
the server receives: "abcdefghijk";
Is it possible for the server to determine and reconstruct strings x and y?
Since the server has no knowledge of the length of either x and y: no.
There are ways to solve this:
Use fixed length fields. In my example x should always be 6 chars and y should always be 5 chars in length. decoding on the server then becomes as trivial as
string x = data.Substring(0, 6)
string y = data.Substring(6, 5)
Use delimiters between the fields. If you are familiar with cvs, the ',' splits the fields. This however has it drawbacks, how to handle a ',' somewhere in a string? The data send over would be like "abcdef,ghijk"
Send the size of each field before the content of the field.
A naive approach just to clarify: string x would be send as '6abcdef' and y as '5ghijk'
Doing all this things by hand can get really hairy and is something that I would consider only if really needed.
I would resort to existing frameworks that do an excellent job on this subject:
Json.net
protobuf ported by Jon skeet
In this case I would first create a class to define the data send to the server instead of a bunch of strings:
class ProcessInfo{
public string ProcessID {get;set;}
public string ProcessName {get;set;}
public string ProcessPath {get;set;}
public string ProcessFileName {get;set;}
public string ProcessMachinename {get;set;}
};
the using Json to serialise this:
var procinfo = new ProcessInfo{
ProcessId = "1",
...
};
var serialised = JsonConvert.SerializeObject(procinfo);
var bytes = Encoding.Utf8.GetBytes(serialised);
ns.Write(bytes, 0, bytes.Length);
And restore it on the server just by:
var procInfo = JsonConvert.DeserializeObject<ProcessInfo>(json);

Check for System DSN and create System DSN if NOT Existing (iSeries Access ODBC Driver)

can someone please help me with this?
i need to check through the System DSN for my ODBC connection to the AS400 servier and create a System DSN if a particular one does not exist.
i've tried googling and have not been able to find anything good for me.
btw, i am quite new to programming. any help will be much appreciated.
thank you
After going through the few less complicated examples available online, this is what i managed to come up with (and it works fine for me).
using System;
using System.Runtime.InteropServices;
public class ODBC_Manager
{
[DllImport("ODBCCP32.dll")]
public static extern bool SQLConfigDataSource(IntPtr parent, int request, string driver, string attributes);
[DllImport("ODBCCP32.dll")]
public static extern int SQLGetPrivateProfileString(string lpszSection, string lpszEntry, string lpszDefault, string #RetBuffer, int cbRetBuffer, string lpszFilename);
private const short ODBC_ADD_DSN = 1;
private const short ODBC_CONFIG_DSN = 2;
private const short ODBC_REMOVE_DSN = 3;
private const short ODBC_ADD_SYS_DSN = 4;
private const short ODBC_CONFIG_SYS_DSN = 5;
private const short ODBC_REMOVE_SYS_DSN = 6;
private const int vbAPINull = 0;
public void CreateDSN(string strDSNName)
{
string strDriver;
string strAttributes;
try
{
string strDSN = "";
string _server = //ip address of the server
string _user = //username
string _pass = //password
string _description = //not required. give a description if you want to
strDriver = "iSeries Access ODBC Driver";
strAttributes = "DSN=" + strDSNName + "\0";
strAttributes += "SYSTEM=" + _server + "\0";
strAttributes += "UID=" + _user + "\0";
strAttributes += "PWD=" + _pass + "\0";
strDSN = strDSN + "System = " + _server + "\n";
strDSN = strDSN + "Description = " + _description + "\n";
if (SQLConfigDataSource((IntPtr)vbAPINull, ODBC_ADD_SYS_DSN, strDriver, strAttributes))
{
Console.WriteLine("DSN was created successfully");
}
else
{
Console.WriteLine("DSN creation failed...");
}
}
catch (Exception ex)
{
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.ToString());
}
else
{
Console.WriteLine(ex.Message.ToString());
}
}
}
public int CheckForDSN(string strDSNName)
{
int iData;
string strRetBuff = "";
iData = SQLGetPrivateProfileString("ODBC Data Sources", strDSNName, "", strRetBuff, 200, "odbc.ini");
return iData;
}
}
... and then call the methods from your application.
static void Main(string[] args)
{
ODBC_Manager odbc = new ODBC_Manager();
string dsnName = //Name of the DSN connection here
if (odbc.CheckForDSN(dsnName) > 0)
{
Console.WriteLine("\n\nODBC Connection " + dsnName + " already exists on the system");
}
else
{
Console.WriteLine("\n\nODBC Connection " + dsnName + " does not exist on the system");
Console.WriteLine("\n\nPress 'Y' to create the connection?");
string cont = Console.ReadLine();
if (cont == "Y" || cont == "y")
{
odbc.CreateDSN(dsnName);
Environment.Exit(1);
}
else
{
Environment.Exit(1);
}
}
}

Categories