switch to using PDU format
this means we can now send { } [ ] and other characters that have multi-byte encoding in the gsm character set
This commit is contained in:
parent
4ca8ddcf9f
commit
b2c4972529
115
sms.fnl
115
sms.fnl
|
@ -26,13 +26,12 @@
|
|||
(string.char i)
|
||||
(.. (string.char (rshift i 8)) (string.char (band i 0xff)))))
|
||||
|
||||
(fn unicode-to-gsm [s]
|
||||
(fn unicode->gsm [s]
|
||||
(s:gsub "." (fn [c] (chars (. gsm-char (string.byte c))))))
|
||||
|
||||
;(print (escape-for-logging (unicode-to-gsm "hello")))
|
||||
;(print (escape-for-logging (unicode-to-gsm "{he@llo}€")))
|
||||
|
||||
|
||||
(fn expect [fd pattern fail-pattern]
|
||||
(let [b (read fd 1024)]
|
||||
(if (> (# b) 0)
|
||||
|
@ -45,6 +44,88 @@
|
|||
(expect fd pattern)))
|
||||
nil)))
|
||||
|
||||
(fn even? [x]
|
||||
(= (% x 2) 0))
|
||||
|
||||
(fn phone-number->hex [number]
|
||||
(let [n (if (even? (# number)) number (.. number "F"))]
|
||||
(n:gsub ".." (fn [s] (.. (s:sub 2 2) (s:sub 1 1))))))
|
||||
|
||||
(assert (= (phone-number->hex "447000123456") "440700214365"))
|
||||
(assert (= (phone-number->hex "85291234567") "5892214365F7"))
|
||||
|
||||
(fn mask [start end]
|
||||
(let [width (+ 1 (- end start))]
|
||||
(lshift (rshift 0xff (- 8 width)) start)))
|
||||
|
||||
(fn bit-range [i start end]
|
||||
(rshift (band i (mask start end)) start))
|
||||
|
||||
|
||||
(fn septets->hex [body]
|
||||
;; 0 body0[0-6] | body1[0] << 7
|
||||
;; 1 body1[1-6] | body2[0-1] << 6
|
||||
;; 2 body2[2-6] | body3[0-2] << 5
|
||||
;; 3 body3[3-6] | body4[0-3] << 4
|
||||
;; 4 body4[4-6] | body5[0-4] << 3
|
||||
;; 5 body5[5-6] | body6[0-5] << 2
|
||||
;; 6 body6[6] | body7[0-6] << 1
|
||||
|
||||
;; 7 body8[0-6] | body9[0] << 7
|
||||
;; 8 body9[1-6] | body10[0-1] << 6
|
||||
|
||||
;; 14 body16[0-6] | body17[0] << 7
|
||||
|
||||
;; for n<7,
|
||||
;; nth byte is bits n..6 of nth septet,
|
||||
;; and bits 0..n of(n+1)th septet
|
||||
|
||||
;; for n>=7,
|
||||
;; bits n%7..6 of floor(n+ (n/7))th septet
|
||||
;; and 0..(n%7) of the next one
|
||||
|
||||
(let [bytes (math.floor (/ (* 7 (# body)) 8))
|
||||
padded (.. body "\0")]
|
||||
(var out "")
|
||||
(for [index 0 bytes]
|
||||
(let [n (% index 7)
|
||||
in-index (math.floor (+ index (/ index 7)))
|
||||
one (bit-range (string.byte padded (+ 1 in-index)) n 6)
|
||||
two (bit-range (string.byte padded (+ 2 in-index)) 0 n)]
|
||||
(set out (string.format "%s%.2X" out
|
||||
(bor one
|
||||
(lshift two (- 7 n) )
|
||||
)))))
|
||||
out))
|
||||
|
||||
(fn message->pdu [destination-number body]
|
||||
;; expects destination-number to be international but no leading +
|
||||
;; body is in gsm 7 bit alphabet
|
||||
(let [fields
|
||||
[
|
||||
;; sms-submit, allow dups, no vaidity period, no rpely path, no udh,
|
||||
;; no reply=path
|
||||
"01"
|
||||
;; ME can choose message reference number
|
||||
"00"
|
||||
(string.format "%.2X" (# destination-number))
|
||||
;; destination-number is international (ITU E.164/E.163) without leading +
|
||||
"91"
|
||||
(phone-number->hex destination-number)
|
||||
;; protocol identifier
|
||||
"00"
|
||||
;; data coding scheme (GSM 7 bit default alphabet)
|
||||
"00"
|
||||
(string.format "%.2X" (# body))
|
||||
(septets->hex body)
|
||||
]]
|
||||
(table.concat fields)))
|
||||
|
||||
;; per worked example at https://www.developershome.com/sms/cmgsCommand4.asp
|
||||
|
||||
(assert (= (message->pdu "85291234567" "It is easy to send text messages.") "01000B915892214365F7000021493A283D0795C3F33C88FE06CDCB6E32885EC6D341EDF27C1E3E97E72E"))
|
||||
|
||||
|
||||
(fn command [fd s]
|
||||
(tx fd (.. s "\r\n"))
|
||||
(expect fd "OK" "ERROR"))
|
||||
|
@ -62,23 +143,23 @@
|
|||
(tcsetattr fd 0 termios)
|
||||
|
||||
(tcdrain fd)
|
||||
(doto fd
|
||||
(command "AT")
|
||||
(command "AT&F") ; revert to defaults
|
||||
(command "ATE0") ; disable command echo
|
||||
(command "AT+CMEE=1") ;print CME errors
|
||||
(command "AT+CMGF=1") ;SMS text mode (vs. PDU mode)
|
||||
(command "AT+CSCS=\"GSM\"") ; default character set
|
||||
(command "AT+CSMP=17,12,0,0") ; message valid for 12*5 minutes
|
||||
(command "AT+CSCA=\"+447958879879\",145\r\n") ;set SMSC
|
||||
|
||||
(tx (.. "AT+CMGS=\"" number "\"\r\n"))
|
||||
(expect ">")
|
||||
(let [pdu (message->pdu number (unicode->gsm body))
|
||||
payload (.. "00" pdu)]
|
||||
(doto fd
|
||||
(command "AT")
|
||||
(command "AT&F") ; revert to defaults
|
||||
(command "ATE0") ; disable command echo
|
||||
(command "AT+CMEE=1") ;print CME errors
|
||||
(command "AT+CSCA=\"+447958879879\",145\r\n") ;set SMSC
|
||||
(command "AT+CMGF=0") ;SMS PDU mode
|
||||
|
||||
;; (tx (unicode-to-gsm body))
|
||||
(tx body)
|
||||
(tx "\026\r\n")
|
||||
(expect "OK"))))
|
||||
(tx (.. "AT+CMGS=" (string.format "%d" (/ (# pdu) 2)) "\r\n"))
|
||||
(expect ">" "ERROR")
|
||||
(tx payload)
|
||||
(tx "\026\r\n")
|
||||
(expect "OK")))))
|
||||
|
||||
;(send-sms "447000123456" "It is never {easy} to [send] text messages.")
|
||||
|
||||
{ :send send-sms }
|
||||
|
|
Loading…
Reference in New Issue