module SerialModem
Constants
- DEBUG_LVL
Attributes
serial_sms[RW]
serial_sms_new[RW]
serial_sms_to_delete[RW]
serial_ussd_new[RW]
Public Instance Methods
attached?()
click to toggle source
# File lib/serialmodem.rb, line 395 def attached? @serial_sp != nil end
check_presence()
click to toggle source
# File lib/serialmodem.rb, line 303 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 log_msg(:SerialModem, "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()
click to toggle source
# File lib/serialmodem.rb, line 235 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()
click to toggle source
# File lib/serialmodem.rb, line 262 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()
click to toggle source
# File lib/serialmodem.rb, line 379 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)
click to toggle source
# File lib/serialmodem.rb, line 108 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)
click to toggle source
# File lib/serialmodem.rb, line 141 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)
click to toggle source
# File lib/serialmodem.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(2) { "found code #{code.inspect} - #{msg.inspect}" } @serial_codes[code] = msg case code when /CMGL/ sms_id, sms_flag, sms_number, sms_unknown, sms_date = msg.scan(/(".*?"|[^",]+\s*|,,)/).flatten ret.push @serial_replies.shift @serial_sms[sms_id] = [sms_flag, sms_number, sms_unknown, sms_date, ret.last] @serial_sms_new.each { |s| s.call(@serial_sms, sms_id) } 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(2) { "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()
click to toggle source
# File lib/serialmodem.rb, line 367 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()
click to toggle source
# File lib/serialmodem.rb, line 132 def save_modem modem_send('AT^U2DIAG=0', 'OK') end
set_connection_type(net, modem = :e303)
click to toggle source
# File lib/serialmodem.rb, line 251 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)
click to toggle source
# File lib/serialmodem.rb, line 14 def setup_modem(dev = nil) @serial_debug = false @serial_tty = @serial_tty_error = @serial_sp = nil @serial_replies = [] @serial_codes = {} @serial_sms = {} @serial_sms_new = [] @serial_sms_to_delete = [] @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 @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()
click to toggle source
# File lib/serialmodem.rb, line 272 def setup_tty check_presence @serial_mutex_rcv.synchronize { if !@serial_sp && @serial_tty if File.exists? @serial_tty log_msg :SerialModem, '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))) log_msg :SerialModem, 'disconnecting modem' kill end } if @serial_sp log_msg :SerialModem, 'initialising modem' init_modem start_serial_thread if !@serial_sp log_msg :SerialModem, 'Lost serial-connection while initialising - trying again' kill reload_option setup_tty return end log_msg :SerialModem, 'finished connecting' end end
sms_delete(number)
click to toggle source
# File lib/serialmodem.rb, line 226 def sms_delete(number) dputs(3) { "Asking to delete #{number} from #{@serial_sms.inspect}" } if @serial_sms.has_key? number dputs(3) { "Deleting #{number}" } modem_send("AT+CMGD=#{number}", 'OK') @serial_sms.delete number end end
sms_scan(force = false)
click to toggle source
# File lib/serialmodem.rb, line 216 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)
click to toggle source
# File lib/serialmodem.rb, line 210 def sms_send(number, msg) modem_send('AT+CMGF=1', 'OK') modem_send("AT+CMGS=\"#{number}\"") modem_send("#{msg}\x1a", 'OK') end
start_serial_thread()
click to toggle source
# File lib/serialmodem.rb, line 331 def start_serial_thread @serial_thread = Thread.new { #dputs_func log_msg :SerialModem, 'Thread started' loop { begin dputs(5) { 'Reading out modem' } if read_reply.length == 0 @serial_sms_to_delete.each { |id| dputs(3) { "Deleting sms #{id} afterwards" } sms_delete(id) } @serial_sms_to_delete = [] 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 end dputs(5) { 'Finished' } } dputs(1) { 'Finished thread' } } end
switch_to_hilink()
click to toggle source
# File lib/serialmodem.rb, line 128 def switch_to_hilink modem_send('AT^U2DIAG=119', 'OK') end
traffic_statistics()
click to toggle source
# File lib/serialmodem.rb, line 258 def traffic_statistics end
ussd_close()
click to toggle source
# File lib/serialmodem.rb, line 161 def ussd_close modem_send("AT+CUSD=2#{@ussd_add}", 'OK') @serial_ussd.length > 0 and ussd_send_now end
ussd_fetch(str)
click to toggle source
# File lib/serialmodem.rb, line 203 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)
click to toggle source
# File lib/serialmodem.rb, line 195 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)
click to toggle source
# File lib/serialmodem.rb, line 166 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()
click to toggle source
# File lib/serialmodem.rb, line 146 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)
click to toggle source
# File lib/serialmodem.rb, line 179 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)
click to toggle source
# File lib/serialmodem.rb, line 136 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