Module: SerialModem
- Extended by:
- HelperClasses::DPuts, SerialModem
- Includes:
- HelperClasses, HelperClasses::DPuts
- Included in:
- SerialModem
- Defined in:
- lib/serial_modem.rb
Constant Summary collapse
- DEBUG_LVL =
1
Instance Attribute Summary collapse
-
#serial_sms ⇒ Object
Returns the value of attribute serial_sms.
-
#serial_sms_new ⇒ Object
Returns the value of attribute serial_sms_new.
-
#serial_ussd_new ⇒ Object
Returns the value of attribute serial_ussd_new.
Instance Method Summary collapse
- #attached? ⇒ Boolean
- #check_presence ⇒ Object
- #get_operator ⇒ Object
- #init_modem ⇒ Object
- #kill ⇒ Object
- #modem_send(str, reply = true) ⇒ Object
- #pdu_to_ussd(str) ⇒ Object
- #read_reply(wait = nil) ⇒ Object
- #reload_option ⇒ Object
- #save_modem ⇒ Object
- #set_connection_type(net, modem = :e303) ⇒ Object
- #setup_modem(dev = nil) ⇒ Object
- #setup_tty ⇒ Object
- #sms_delete(number) ⇒ Object
- #sms_new(id, flag, number, date, msg, unknown = nil) ⇒ Object
- #sms_scan(force = false) ⇒ Object
- #sms_send(number, msg) ⇒ Object
- #start_serial_thread ⇒ Object
- #switch_to_hilink ⇒ Object
- #traffic_statistics ⇒ Object
- #ussd_close ⇒ Object
- #ussd_fetch(str) ⇒ Object
- #ussd_received(str) ⇒ Object
- #ussd_send(str) ⇒ Object
- #ussd_send_now ⇒ Object
- #ussd_store_result(str) ⇒ Object
- #ussd_to_pdu(str) ⇒ Object
Instance Attribute Details
#serial_sms ⇒ Object
Returns the value of attribute serial_sms.
6 7 8 |
# File 'lib/serial_modem.rb', line 6 def serial_sms @serial_sms end |
#serial_sms_new ⇒ Object
Returns the value of attribute serial_sms_new.
6 7 8 |
# File 'lib/serial_modem.rb', line 6 def serial_sms_new @serial_sms_new end |
#serial_ussd_new ⇒ Object
Returns the value of attribute serial_ussd_new.
6 7 8 |
# File 'lib/serial_modem.rb', line 6 def serial_ussd_new @serial_ussd_new end |
Instance Method Details
#attached? ⇒ Boolean
414 415 416 |
# File 'lib/serial_modem.rb', line 414 def attached? @serial_sp != nil end |
#check_presence ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/serial_modem.rb', line 319 def check_presence @serial_mutex_rcv.synchronize { @serial_tty.to_s.length > 0 and File.exists?(@serial_tty) and return case lsusb = System.run_str('lsusb') when /12d1:1506/, /12d1:14ac/, /12d1:1c05/ log_msg :SerialModem, 'Found 3G-modem with ttyUSB0-ttyUSB2' @serial_tty_error = '/dev/ttyUSB3' @serial_tty = '/dev/ttyUSB2' @ussd_add = (lsusb =~ /12d1:14ac/) ? ',15' : '' @serial_eats_sms = true when /airtel-modem/ log_msg :SerialModem, 'Found 3G-modem with ttyUSB0-ttyUSB4' @serial_tty_error = '/dev/ttyUSB5' @serial_tty = '/dev/ttyUSB4' @ussd_add = '' else #puts caller.join("\n") @serial_tty = @serial_tty_error = nil end dputs(2){"serial_tty is #{@serial_tty.inspect} and exists " + "#{File.exists?(@serial_tty.to_s)}"} if @serial_tty_error && File.exists?(@serial_tty_error) log_msg :SerialModem, 'resetting modem' reload_option end } end |
#get_operator ⇒ Object
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/serial_modem.rb', line 251 def get_operator modem_send('AT+COPS=3,0', 'OK') modem_send('AT+COPS?', 'OK') (1..6).each { if @serial_codes.has_key? 'COPS' return '' if @serial_codes['COPS'] == '0' @serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK') op = @serial_codes['COPS'].scan(/".*?"|[^",]\s*|,,/)[2].gsub(/"/, '') dputs(2) { "Found operator-string #{op}" } return op end sleep 0.5 } return '' end |
#init_modem ⇒ Object
278 279 280 281 282 283 284 285 286 |
# File 'lib/serial_modem.rb', line 278 def init_modem %w( ATZ AT+CNMI=0,0,0,0,0 AT+CPMS="SM","SM","SM" AT+CFUN=1 AT+CMGF=1 ).each { |at| modem_send(at, 'OK') } @serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK') set_connection_type '3g' end |
#kill ⇒ Object
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/serial_modem.rb', line 398 def kill #dputs_func if @serial_thread if @serial_thread.alive? dputs(3) { 'Killing thread' } @serial_thread.kill dputs(3) { 'Joining thread' } @serial_thread.join dputs(3) { 'Thread joined' } end end @serial_sp and @serial_sp.close dputs(1) { 'SerialModem killed' } @serial_sp = nil end |
#modem_send(str, reply = true) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/serial_modem.rb', line 123 def modem_send(str, reply = true) return unless @serial_sp @serial_debug and dputs_func dputs(3) { "Sending string #{str} to modem" } @serial_mutex_send.synchronize { begin @serial_sp.write("#{str}\r\n") rescue Errno::EIO => e log_msg :SerialModem, "Couldn't write to device" kill return rescue Errno::ENODEV => e log_msg :SerialModem, 'Device is not here anymore' kill return end } read_reply(reply) end |
#pdu_to_ussd(str) ⇒ Object
156 157 158 159 |
# File 'lib/serial_modem.rb', line 156 def pdu_to_ussd(str) [str].pack('H*').unpack('b*').join.scan(/.{7}/). map { |s| [s+"0"].pack('b*') }.join end |
#read_reply(wait = nil) ⇒ Object
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 |
# File 'lib/serial_modem.rb', line 38 def read_reply(wait = nil) @serial_debug and dputs_func raise IOError.new('NoModemHere') unless @serial_sp ret = [] begin @serial_mutex_rcv.synchronize { while !@serial_sp.eof? || wait begin @serial_replies.push rep = @serial_sp.readline.chomp break if rep == wait rescue EOFError => e dputs(4) { 'Waited for string, but got nothing' } break end end } while m = @serial_replies.shift @serial_debug and dputs_func next if (m == '' || m =~ /^\^/) dputs(3) { "Reply: #{m}" } ret.push m if m =~ /\+[\w]{4}: / code, msg = m[1..4], m[7..-1] dputs(3) { "found code #{code.inspect} - #{msg.inspect}" } @serial_codes[code] = msg case code when /CMGL/ # Typical input from the modem: # "0,\"REC UNREAD\",\"+23599836457\",,\"15/04/08,17:12:21+04\"" # Output desired: # ["0", "REC UNREAD", "+23599836457", "", "15/04/08,17:12:21+04"] id, flag, number, unknown, date = msg.scan(/"(.*?)"|([^",]+)\s*|,,/).collect { |a, b| a.to_s + b.to_s } ret.push @serial_replies.shift sms_new(id, flag, number, date, ret.last, unknown) when /CUSD/ if pdu = msg.match(/.*\"(.*)\".*/) ussd_received(pdu_to_ussd(pdu[1])) elsif msg == '2' #log_msg :serialmodem, 'Closed USSD.' #ussd_received('') #ussd_close else log_msg :serialmodem, "Unknown: CUSD - #{msg}" end when /CMTI/ if msg =~ /^.ME.,/ dputs(3) { "I think I got a new message: #{msg}" } sms_scan true else log_msg :serialmodem, "Unknown: CMTI - #{msg}" end @serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK') # Probably a message or so - '+CMTI: "ME",0' is a new message end end end rescue IOError => e raise e =begin rescue Exception => e puts "#{e.inspect}" puts "#{e.to_s}" puts e.backtrace =end end ret end |
#reload_option ⇒ Object
386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/serial_modem.rb', line 386 def reload_option @serial_sp and @serial_sp.close @serial_sp = nil dputs(1) { 'Trying to reload modem-driver - killing and reloading' } %w( chat ppp).each { |pro| System.run_str("killall -9 #{pro}") } %w(rmmod modprobe).each { |cmd| System.run_str("#{cmd} option") } end |
#save_modem ⇒ Object
147 148 149 |
# File 'lib/serial_modem.rb', line 147 def save_modem modem_send('AT^U2DIAG=0', 'OK') end |
#set_connection_type(net, modem = :e303) ⇒ Object
267 268 269 270 271 272 |
# File 'lib/serial_modem.rb', line 267 def set_connection_type(net, modem = :e303) # According to https://wiki.archlinux.org/index.php/3G_and_GPRS_modems_with_pppd cmds = {e303: {c3go: '14,2,3FFFFFFF,0,2', c3g: '2,2,3FFFFFFF,0,2', c2go: '13,1,3FFFFFFF,0,2', c2g: '2,1,3FFFFFFF,0,2'}} modem_send "AT^SYSCFG=#{cmds[modem]["c#{net}".to_sym]}", 'OK' end |
#setup_modem(dev = nil) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/serial_modem.rb', line 13 def setup_modem(dev = nil) @serial_debug = false @serial_tty = @serial_tty_error = @serial_sp = nil @serial_replies = [] @serial_codes = {} @serial_sms = {} # TODO: once serialmodem == class, change this into Observer @serial_sms_new = [] @serial_sms_autoscan = 20 @serial_sms_autoscan_last = Time.now @serial_ussd = [] @serial_ussd_last = Time.now @serial_ussd_timeout = 30 @serial_ussd_results = [] @serial_ussd_results_max = 100 # TODO: once serialmodem == class, change this into Observer @serial_ussd_new = [] @serial_mutex_rcv = Mutex.new @serial_mutex_send = Mutex.new # Some Huawei-modems eat SMS once they send a +CMTI-message - this # turns off the CMTI-messages which slows down incoming SMS detection @serial_eats_sms = false setup_tty end |
#setup_tty ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/serial_modem.rb', line 288 def setup_tty check_presence @serial_mutex_rcv.synchronize { if !@serial_sp && @serial_tty if File.exists? @serial_tty dputs(2){ 'setting up SerialPort'} @serial_sp = SerialPort.new(@serial_tty, 115200) @serial_sp.read_timeout = 500 end elsif @serial_sp && (!@serial_tty||(@serial_tty && !File.exists?(@serial_tty))) dputs(2){'disconnecting modem'} kill end } if @serial_sp dputs(2){'initialising modem'} init_modem start_serial_thread if !@serial_sp dputs(2){'Lost serial-connection while initialising - trying again'} kill reload_option setup_tty return end dputs(2){'finished connecting'} end end |
#sms_delete(number) ⇒ Object
242 243 244 245 246 247 248 249 |
# File 'lib/serial_modem.rb', line 242 def sms_delete(number) dputs(3) { "Asking to delete #{number} from #{@serial_sms.inspect}" } if @serial_sms.has_key? number.to_s dputs(3) { "Deleting #{number}" } modem_send("AT+CMGD=#{number}", 'OK') @serial_sms.delete number.to_s end end |
#sms_new(id, flag, number, date, msg, unknown = nil) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/serial_modem.rb', line 108 def sms_new(id, flag, number, date, msg, unknown = nil) sms = {flag: flag, number: number, unknown: unknown, date: date, msg: msg, id: id} @serial_sms[id.to_s] = sms if flag =~ /unread/i log_msg :SerialModem, "New SMS: #{sms.inspect}" rescue_all do @serial_sms_new.each { |s| s.call(sms) } end end sms end |
#sms_scan(force = false) ⇒ Object
232 233 234 235 236 237 238 239 240 |
# File 'lib/serial_modem.rb', line 232 def sms_scan(force = false) if force || (@serial_sms_autoscan > 0 && Time.now - @serial_sms_autoscan_last > @serial_sms_autoscan) dputs(3) { 'Auto-scanning sms' } @serial_sms_autoscan_last = Time.now modem_send('AT+CMGF=1', 'OK') modem_send('AT+CMGL="ALL"', 'OK') end end |
#sms_send(number, msg) ⇒ Object
225 226 227 228 229 230 |
# File 'lib/serial_modem.rb', line 225 def sms_send(number, msg) log_msg :SerialModem, "Sending SMS --#{msg.inspect}-- to --#{number.inspect}--" modem_send('AT+CMGF=1', 'OK') modem_send("AT+CMGS=\"#{number}\"") modem_send("#{msg}\x1a", 'OK') end |
#start_serial_thread ⇒ Object
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/serial_modem.rb', line 347 def start_serial_thread @serial_thread = Thread.new { #dputs_func dputs(2){'Thread started'} loop { begin dputs(5) { 'Reading out modem' } if read_reply.length == 0 #@serial_sms.each { |id, sms| # ddputs(3) { "Deleting sms #{sms.inspect} afterwards" } # sms_delete(id) #} end dputs(4) { (Time.now - @serial_ussd_last).to_s } if (Time.now - @serial_ussd_last > @serial_ussd_timeout) && (@serial_ussd.length > 0) log_msg :SerialModem, "Re-sending #{@serial_ussd.first}" ussd_send_now end sms_scan sleep 0.5 rescue IOError log_msg :SerialModem, 'IOError - killing modem' kill return rescue Exception => e dputs(0) { "#{e.inspect}" } dputs(0) { "#{e.to_s}" } e.backtrace.each { |l| dputs(0) { l } } end dputs(5) { 'Finished' } } dputs(1) { 'Finished thread' } } end |
#switch_to_hilink ⇒ Object
143 144 145 |
# File 'lib/serial_modem.rb', line 143 def switch_to_hilink modem_send('AT^U2DIAG=119', 'OK') end |
#traffic_statistics ⇒ Object
274 275 276 |
# File 'lib/serial_modem.rb', line 274 def traffic_statistics end |
#ussd_close ⇒ Object
176 177 178 179 |
# File 'lib/serial_modem.rb', line 176 def ussd_close modem_send("AT+CUSD=2#{@ussd_add}", 'OK') @serial_ussd.length > 0 and ussd_send_now end |
#ussd_fetch(str) ⇒ Object
218 219 220 221 222 223 |
# File 'lib/serial_modem.rb', line 218 def ussd_fetch(str) return nil unless @serial_ussd_results dputs(3) { "Fetching str #{str} - #{@serial_ussd_results.inspect}" } res = @serial_ussd_results.reverse.find { |u| u._code == str } res ? res._result : nil end |
#ussd_received(str) ⇒ Object
210 211 212 213 214 215 216 |
# File 'lib/serial_modem.rb', line 210 def ussd_received(str) code = ussd_store_result(str) dputs(2) { "Got result for #{code}: -#{str}-" } @serial_ussd_new.each { |s| s.call(code, str) } end |
#ussd_send(str) ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/serial_modem.rb', line 181 def ussd_send(str) if str.class == String dputs(3) { "Sending ussd-code #{str}" } @serial_ussd.push str @serial_ussd.length == 1 and ussd_send_now elsif str.class == Array dputs(3) { "Sending menu-command #{str}" } @serial_ussd.concat str @serial_ussd.push nil @serial_ussd.length == str.length + 1 and ussd_send_now end end |
#ussd_send_now ⇒ Object
161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/serial_modem.rb', line 161 def ussd_send_now return unless @serial_ussd.length > 0 str_send = @serial_ussd.first @serial_ussd_last = Time.now if str_send #log_msg :SerialModem, "Sending ussd-string #{str_send} with add of #{@ussd_add} "+ "and queue #{@serial_ussd}" modem_send("AT+CUSD=1,\"#{ussd_to_pdu(str_send)}\"#{@ussd_add}", 'OK') else dputs(2) { 'Sending ussd-close' } @serial_ussd.shift ussd_close end end |
#ussd_store_result(str) ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/serial_modem.rb', line 194 def ussd_store_result(str) if @serial_ussd.length > 0 code = @serial_ussd.shift dputs(2) { "Got USSD-reply for #{code}: #{str}" } @serial_ussd_results.push(time: Time.now.strftime('%H:%M'), code: code, result: str) @serial_ussd_results.shift([0, @serial_ussd_results.length - @serial_ussd_results_max].max) ussd_send_now code else #log_msg :serialmodem, "Got unasked code #{str}" 'unknown' end end |
#ussd_to_pdu(str) ⇒ Object
151 152 153 154 |
# File 'lib/serial_modem.rb', line 151 def ussd_to_pdu(str) str.unpack('b*').join.scan(/.{8}/).map { |s| s[0..6] }.join. scan(/.{1,8}/).map { |s| [s].pack('b*').unpack('H*')[0].upcase }.join end |