serial_modem 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +3 -0
- data/README.md +139 -0
- data/Rakefile +2 -0
- data/commit +17 -0
- data/lib/serial_modem.rb +418 -0
- data/serial_modem.gemspec +23 -0
- data/test/run_test +10 -0
- data/test/test_hw.rb +59 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4f4ad510a17d04cf960fbb07b9fee8da5e3a705d
|
4
|
+
data.tar.gz: c561195ce7439aa7a25214caeb3f4ead70ba227c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: edee72d6fff2ebd4f98b2a887c45a09a4c2c2f6ae0fab8b6773450bf187cf3615c0710037190efe30e71970a2427f273c3d0e6d71f929c2de4e2f497181499fe
|
7
|
+
data.tar.gz: b0ca14f55324f823432c626178af5bea5680a5758022d98c99213dc8d4f98d52b28dfd39529f50618d78e87850b0daee7f9fbd65648d21b638949b7633fbfd95
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# SerialModem
|
2
|
+
|
3
|
+
Simple interface for Serial Modems. Tested:
|
4
|
+
- Huawei E303 in serial mode
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'SerialModem'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install SerialModem
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Simple
|
25
|
+
|
26
|
+
If everything is connected and the modem is recognized (/dev/ttyUSB* is
|
27
|
+
available), then you can simply do:
|
28
|
+
|
29
|
+
```
|
30
|
+
require 'serialmodem'
|
31
|
+
|
32
|
+
SerialModem::setup_modem
|
33
|
+
return unless SerialModem.attached?
|
34
|
+
|
35
|
+
SerialModem::ussd_send('*100#')
|
36
|
+
SerialModem::sms_send('+23599999999', 'Hello from SerialModem')
|
37
|
+
```
|
38
|
+
|
39
|
+
### Ussd
|
40
|
+
|
41
|
+
Huawei-modems with Hilink don't support USSD, so you have to switch them
|
42
|
+
to serial-mode. Older modems (pre-2015) are handled with _HilinkModem_, newer versions
|
43
|
+
not yet. Once the modem is in serial-mode, you can send and receive USSD-codes.
|
44
|
+
It is even possible to use USSD-menus.
|
45
|
+
|
46
|
+
#### Sending
|
47
|
+
|
48
|
+
Supposing the modem is setup, you can do
|
49
|
+
|
50
|
+
```
|
51
|
+
SerialModem::send_ussd('*100#')
|
52
|
+
```
|
53
|
+
|
54
|
+
Or, if you need a menu where each step needs an answer, you can do
|
55
|
+
|
56
|
+
```
|
57
|
+
SerialModem::send_ussd('*800#', '1', '2', '1234')
|
58
|
+
```
|
59
|
+
|
60
|
+
Now each command waits for the last command to be completed.
|
61
|
+
|
62
|
+
#### Receiving
|
63
|
+
|
64
|
+
The received codes are stored in an array of hashes, where each hash has
|
65
|
+
three fields:
|
66
|
+
|
67
|
+
- time: a string of "%H:%M"
|
68
|
+
- code: the ussd-code as sent out by 'ussd_send'
|
69
|
+
- result
|
70
|
+
|
71
|
+
There is a maximum of _SerialModem::serial_ussd_results_max_ messages
|
72
|
+
stored.
|
73
|
+
|
74
|
+
```
|
75
|
+
SerialModem::send_ussd('*128#')
|
76
|
+
sleep 10
|
77
|
+
result = SerialModem.serial_ussd_results.first.result
|
78
|
+
```
|
79
|
+
|
80
|
+
#### Asynchronous receiving
|
81
|
+
|
82
|
+
You can also define a listener in _SerialModem.serial_ussd_new_
|
83
|
+
|
84
|
+
### SMS
|
85
|
+
|
86
|
+
Similar to USSD, you can send and receive SMS. Due to some restrictions in
|
87
|
+
Huawei-modems, there is a thread that checks for new SMSs every 20s. Normally
|
88
|
+
modems should reply as soon as an SMS is received, but most of the Huawei-modems
|
89
|
+
tested delete SMS automatically when in this mode. If you prefer nonethelss to
|
90
|
+
rely on this mode, set _SerialModem.serial_sms_autoscan_ to 0.
|
91
|
+
|
92
|
+
#### Sending
|
93
|
+
|
94
|
+
If everything is recognized, simply do:
|
95
|
+
|
96
|
+
```
|
97
|
+
SerialModem.sms_send('+23599999999', 'Hello from SerialModem')
|
98
|
+
```
|
99
|
+
|
100
|
+
And the message should be sent.
|
101
|
+
|
102
|
+
#### Receiving
|
103
|
+
|
104
|
+
All SMS are put in a hash of arrays with the key of the hash being the message-id
|
105
|
+
and the elements of the array as follows:
|
106
|
+
|
107
|
+
0: sms-flag
|
108
|
+
1: number of sender
|
109
|
+
2: unknown field
|
110
|
+
3: date and time of SMS
|
111
|
+
4: the message
|
112
|
+
|
113
|
+
#### Asynchronous receiving
|
114
|
+
|
115
|
+
You can also use the _SerialModem.serial_sms_new_ variable to set up an
|
116
|
+
automatic callback whenever a new SMS is received:
|
117
|
+
|
118
|
+
```
|
119
|
+
|
120
|
+
def treat_sms(list, id)
|
121
|
+
p "Received SMS from #{list[id][1]}"
|
122
|
+
end
|
123
|
+
|
124
|
+
SerialModem.serial_sms_new.push(Proc.new { |list, id| treat_sms(list, id) })
|
125
|
+
|
126
|
+
# Wait for SMS
|
127
|
+
```
|
128
|
+
|
129
|
+
## Special
|
130
|
+
|
131
|
+
Some care has been taken that the serial modem is recognized and can be
|
132
|
+
functional again in case of error:
|
133
|
+
|
134
|
+
### detach and re-attach in case of power-failure
|
135
|
+
|
136
|
+
It can happen that the modem is in use and is reattached because of errors in the
|
137
|
+
power. In that case it is not attached anymore to '/dev/ttyUSB2', but to
|
138
|
+
'/dev/ttyUSB3'. With some luck, the _SerialModem.reload_option_ can help to
|
139
|
+
make things OK again.
|
data/Rakefile
ADDED
data/commit
ADDED
data/lib/serial_modem.rb
ADDED
@@ -0,0 +1,418 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
require 'helperclasses'
|
3
|
+
|
4
|
+
module SerialModem
|
5
|
+
DEBUG_LVL = 1
|
6
|
+
attr_accessor :serial_sms_new, :serial_sms,
|
7
|
+
:serial_ussd_new
|
8
|
+
extend self
|
9
|
+
include HelperClasses
|
10
|
+
include HelperClasses::DPuts
|
11
|
+
extend HelperClasses::DPuts
|
12
|
+
|
13
|
+
def setup_modem(dev = nil)
|
14
|
+
@serial_debug = false
|
15
|
+
@serial_tty = @serial_tty_error = @serial_sp = nil
|
16
|
+
@serial_replies = []
|
17
|
+
@serial_codes = {}
|
18
|
+
@serial_sms = {}
|
19
|
+
# TODO: once serialmodem == class, change this into Observer
|
20
|
+
@serial_sms_new = []
|
21
|
+
@serial_sms_autoscan = 20
|
22
|
+
@serial_sms_autoscan_last = Time.now
|
23
|
+
@serial_ussd = []
|
24
|
+
@serial_ussd_last = Time.now
|
25
|
+
@serial_ussd_timeout = 30
|
26
|
+
@serial_ussd_results = []
|
27
|
+
@serial_ussd_results_max = 100
|
28
|
+
# TODO: once serialmodem == class, change this into Observer
|
29
|
+
@serial_ussd_new = []
|
30
|
+
@serial_mutex_rcv = Mutex.new
|
31
|
+
@serial_mutex_send = Mutex.new
|
32
|
+
# Some Huawei-modems eat SMS once they send a +CMTI-message - this
|
33
|
+
# turns off the CMTI-messages which slows down incoming SMS detection
|
34
|
+
@serial_eats_sms = false
|
35
|
+
setup_tty
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_reply(wait = nil)
|
39
|
+
@serial_debug and dputs_func
|
40
|
+
raise IOError.new('NoModemHere') unless @serial_sp
|
41
|
+
ret = []
|
42
|
+
begin
|
43
|
+
@serial_mutex_rcv.synchronize {
|
44
|
+
while !@serial_sp.eof? || wait
|
45
|
+
begin
|
46
|
+
@serial_replies.push rep = @serial_sp.readline.chomp
|
47
|
+
break if rep == wait
|
48
|
+
rescue EOFError => e
|
49
|
+
dputs(4) { 'Waited for string, but got nothing' }
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
}
|
54
|
+
|
55
|
+
while m = @serial_replies.shift
|
56
|
+
@serial_debug and dputs_func
|
57
|
+
next if (m == '' || m =~ /^\^/)
|
58
|
+
dputs(3) { "Reply: #{m}" }
|
59
|
+
ret.push m
|
60
|
+
if m =~ /\+[\w]{4}: /
|
61
|
+
code, msg = m[1..4], m[7..-1]
|
62
|
+
dputs(3) { "found code #{code.inspect} - #{msg.inspect}" }
|
63
|
+
@serial_codes[code] = msg
|
64
|
+
case code
|
65
|
+
when /CMGL/
|
66
|
+
# Typical input from the modem:
|
67
|
+
# "0,\"REC UNREAD\",\"+23599836457\",,\"15/04/08,17:12:21+04\""
|
68
|
+
# Output desired:
|
69
|
+
# ["0", "REC UNREAD", "+23599836457", "", "15/04/08,17:12:21+04"]
|
70
|
+
id, flag, number, unknown, date =
|
71
|
+
msg.scan(/"(.*?)"|([^",]+)\s*|,,/).collect { |a, b| a.to_s + b.to_s }
|
72
|
+
ret.push @serial_replies.shift
|
73
|
+
sms_new(id, flag, number, date, ret.last, unknown)
|
74
|
+
when /CUSD/
|
75
|
+
if pdu = msg.match(/.*\"(.*)\".*/)
|
76
|
+
ussd_received(pdu_to_ussd(pdu[1]))
|
77
|
+
elsif msg == '2'
|
78
|
+
#log_msg :serialmodem, 'Closed USSD.'
|
79
|
+
#ussd_received('')
|
80
|
+
#ussd_close
|
81
|
+
else
|
82
|
+
log_msg :serialmodem, "Unknown: CUSD - #{msg}"
|
83
|
+
end
|
84
|
+
when /CMTI/
|
85
|
+
if msg =~ /^.ME.,/
|
86
|
+
dputs(3) { "I think I got a new message: #{msg}" }
|
87
|
+
sms_scan true
|
88
|
+
else
|
89
|
+
log_msg :serialmodem, "Unknown: CMTI - #{msg}"
|
90
|
+
end
|
91
|
+
@serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK')
|
92
|
+
# Probably a message or so - '+CMTI: "ME",0' is a new message
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue IOError => e
|
97
|
+
raise e
|
98
|
+
=begin
|
99
|
+
rescue Exception => e
|
100
|
+
puts "#{e.inspect}"
|
101
|
+
puts "#{e.to_s}"
|
102
|
+
puts e.backtrace
|
103
|
+
=end
|
104
|
+
end
|
105
|
+
ret
|
106
|
+
end
|
107
|
+
|
108
|
+
def sms_new(id, flag, number, date, msg, unknown = nil)
|
109
|
+
sms = {flag: flag, number: number, unknown: unknown, date: date,
|
110
|
+
msg: msg, id: id}
|
111
|
+
@serial_sms[id.to_s] = sms
|
112
|
+
if flag =~ /unread/i
|
113
|
+
log_msg :SerialModem, "New SMS: #{sms.inspect}"
|
114
|
+
rescue_all do
|
115
|
+
@serial_sms_new.each { |s|
|
116
|
+
s.call(sms)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
sms
|
121
|
+
end
|
122
|
+
|
123
|
+
def modem_send(str, reply = true)
|
124
|
+
return unless @serial_sp
|
125
|
+
@serial_debug and dputs_func
|
126
|
+
dputs(3) { "Sending string #{str} to modem" }
|
127
|
+
@serial_mutex_send.synchronize {
|
128
|
+
begin
|
129
|
+
@serial_sp.write("#{str}\r\n")
|
130
|
+
rescue Errno::EIO => e
|
131
|
+
log_msg :SerialModem, "Couldn't write to device"
|
132
|
+
kill
|
133
|
+
return
|
134
|
+
rescue Errno::ENODEV => e
|
135
|
+
log_msg :SerialModem, 'Device is not here anymore'
|
136
|
+
kill
|
137
|
+
return
|
138
|
+
end
|
139
|
+
}
|
140
|
+
read_reply(reply)
|
141
|
+
end
|
142
|
+
|
143
|
+
def switch_to_hilink
|
144
|
+
modem_send('AT^U2DIAG=119', 'OK')
|
145
|
+
end
|
146
|
+
|
147
|
+
def save_modem
|
148
|
+
modem_send('AT^U2DIAG=0', 'OK')
|
149
|
+
end
|
150
|
+
|
151
|
+
def ussd_to_pdu(str)
|
152
|
+
str.unpack('b*').join.scan(/.{8}/).map { |s| s[0..6] }.join.
|
153
|
+
scan(/.{1,8}/).map { |s| [s].pack('b*').unpack('H*')[0].upcase }.join
|
154
|
+
end
|
155
|
+
|
156
|
+
def pdu_to_ussd(str)
|
157
|
+
[str].pack('H*').unpack('b*').join.scan(/.{7}/).
|
158
|
+
map { |s| [s+"0"].pack('b*') }.join
|
159
|
+
end
|
160
|
+
|
161
|
+
def ussd_send_now
|
162
|
+
return unless @serial_ussd.length > 0
|
163
|
+
str_send = @serial_ussd.first
|
164
|
+
@serial_ussd_last = Time.now
|
165
|
+
if str_send
|
166
|
+
#log_msg :SerialModem, "Sending ussd-string #{str_send} with add of #{@ussd_add} "+
|
167
|
+
"and queue #{@serial_ussd}"
|
168
|
+
modem_send("AT+CUSD=1,\"#{ussd_to_pdu(str_send)}\"#{@ussd_add}", 'OK')
|
169
|
+
else
|
170
|
+
dputs(2) { 'Sending ussd-close' }
|
171
|
+
@serial_ussd.shift
|
172
|
+
ussd_close
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def ussd_close
|
177
|
+
modem_send("AT+CUSD=2#{@ussd_add}", 'OK')
|
178
|
+
@serial_ussd.length > 0 and ussd_send_now
|
179
|
+
end
|
180
|
+
|
181
|
+
def ussd_send(str)
|
182
|
+
if str.class == String
|
183
|
+
dputs(3) { "Sending ussd-code #{str}" }
|
184
|
+
@serial_ussd.push str
|
185
|
+
@serial_ussd.length == 1 and ussd_send_now
|
186
|
+
elsif str.class == Array
|
187
|
+
dputs(3) { "Sending menu-command #{str}" }
|
188
|
+
@serial_ussd.concat str
|
189
|
+
@serial_ussd.push nil
|
190
|
+
@serial_ussd.length == str.length + 1 and ussd_send_now
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def ussd_store_result(str)
|
195
|
+
if @serial_ussd.length > 0
|
196
|
+
code = @serial_ussd.shift
|
197
|
+
dputs(2) { "Got USSD-reply for #{code}: #{str}" }
|
198
|
+
@serial_ussd_results.push(time: Time.now.strftime('%H:%M'),
|
199
|
+
code: code, result: str)
|
200
|
+
@serial_ussd_results.shift([0, @serial_ussd_results.length -
|
201
|
+
@serial_ussd_results_max].max)
|
202
|
+
ussd_send_now
|
203
|
+
code
|
204
|
+
else
|
205
|
+
#log_msg :serialmodem, "Got unasked code #{str}"
|
206
|
+
'unknown'
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def ussd_received(str)
|
211
|
+
code = ussd_store_result(str)
|
212
|
+
dputs(2) { "Got result for #{code}: -#{str}-" }
|
213
|
+
@serial_ussd_new.each { |s|
|
214
|
+
s.call(code, str)
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
def ussd_fetch(str)
|
219
|
+
return nil unless @serial_ussd_results
|
220
|
+
dputs(3) { "Fetching str #{str} - #{@serial_ussd_results.inspect}" }
|
221
|
+
res = @serial_ussd_results.reverse.find { |u| u._code == str }
|
222
|
+
res ? res._result : nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def sms_send(number, msg)
|
226
|
+
log_msg :SerialModem, "Sending SMS --#{msg.inspect}-- to --#{number.inspect}--"
|
227
|
+
modem_send('AT+CMGF=1', 'OK')
|
228
|
+
modem_send("AT+CMGS=\"#{number}\"")
|
229
|
+
modem_send("#{msg}\x1a", 'OK')
|
230
|
+
end
|
231
|
+
|
232
|
+
def sms_scan(force = false)
|
233
|
+
if force || (@serial_sms_autoscan > 0 &&
|
234
|
+
Time.now - @serial_sms_autoscan_last > @serial_sms_autoscan)
|
235
|
+
dputs(3) { 'Auto-scanning sms' }
|
236
|
+
@serial_sms_autoscan_last = Time.now
|
237
|
+
modem_send('AT+CMGF=1', 'OK')
|
238
|
+
modem_send('AT+CMGL="ALL"', 'OK')
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def sms_delete(number)
|
243
|
+
dputs(3) { "Asking to delete #{number} from #{@serial_sms.inspect}" }
|
244
|
+
if @serial_sms.has_key? number.to_s
|
245
|
+
dputs(3) { "Deleting #{number}" }
|
246
|
+
modem_send("AT+CMGD=#{number}", 'OK')
|
247
|
+
@serial_sms.delete number.to_s
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def get_operator
|
252
|
+
modem_send('AT+COPS=3,0', 'OK')
|
253
|
+
modem_send('AT+COPS?', 'OK')
|
254
|
+
(1..6).each {
|
255
|
+
if @serial_codes.has_key? 'COPS'
|
256
|
+
return '' if @serial_codes['COPS'] == '0'
|
257
|
+
@serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK')
|
258
|
+
op = @serial_codes['COPS'].scan(/".*?"|[^",]\s*|,,/)[2].gsub(/"/, '')
|
259
|
+
dputs(2) { "Found operator-string #{op}" }
|
260
|
+
return op
|
261
|
+
end
|
262
|
+
sleep 0.5
|
263
|
+
}
|
264
|
+
return ''
|
265
|
+
end
|
266
|
+
|
267
|
+
def set_connection_type(net, modem = :e303)
|
268
|
+
# According to https://wiki.archlinux.org/index.php/3G_and_GPRS_modems_with_pppd
|
269
|
+
cmds = {e303: {c3go: '14,2,3FFFFFFF,0,2', c3g: '2,2,3FFFFFFF,0,2',
|
270
|
+
c2go: '13,1,3FFFFFFF,0,2', c2g: '2,1,3FFFFFFF,0,2'}}
|
271
|
+
modem_send "AT^SYSCFG=#{cmds[modem]["c#{net}".to_sym]}", 'OK'
|
272
|
+
end
|
273
|
+
|
274
|
+
def traffic_statistics
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
def init_modem
|
279
|
+
%w( ATZ
|
280
|
+
AT+CNMI=0,0,0,0,0
|
281
|
+
AT+CPMS="SM","SM","SM"
|
282
|
+
AT+CFUN=1
|
283
|
+
AT+CMGF=1 ).each { |at| modem_send(at, 'OK') }
|
284
|
+
@serial_eats_sms and modem_send('AT+CNMI=0,0,0,0,0', 'OK')
|
285
|
+
set_connection_type '3g'
|
286
|
+
end
|
287
|
+
|
288
|
+
def setup_tty
|
289
|
+
check_presence
|
290
|
+
|
291
|
+
@serial_mutex_rcv.synchronize {
|
292
|
+
if !@serial_sp && @serial_tty
|
293
|
+
if File.exists? @serial_tty
|
294
|
+
dputs(2){ 'setting up SerialPort'}
|
295
|
+
@serial_sp = SerialPort.new(@serial_tty, 115200)
|
296
|
+
@serial_sp.read_timeout = 500
|
297
|
+
end
|
298
|
+
elsif @serial_sp &&
|
299
|
+
(!@serial_tty||(@serial_tty && !File.exists?(@serial_tty)))
|
300
|
+
dputs(2){'disconnecting modem'}
|
301
|
+
kill
|
302
|
+
end
|
303
|
+
}
|
304
|
+
if @serial_sp
|
305
|
+
dputs(2){'initialising modem'}
|
306
|
+
init_modem
|
307
|
+
start_serial_thread
|
308
|
+
if !@serial_sp
|
309
|
+
dputs(2){'Lost serial-connection while initialising - trying again'}
|
310
|
+
kill
|
311
|
+
reload_option
|
312
|
+
setup_tty
|
313
|
+
return
|
314
|
+
end
|
315
|
+
dputs(2){'finished connecting'}
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def check_presence
|
320
|
+
@serial_mutex_rcv.synchronize {
|
321
|
+
@serial_tty.to_s.length > 0 and File.exists?(@serial_tty) and return
|
322
|
+
case lsusb = System.run_str('lsusb')
|
323
|
+
when /12d1:1506/, /12d1:14ac/, /12d1:1c05/
|
324
|
+
log_msg :SerialModem, 'Found 3G-modem with ttyUSB0-ttyUSB2'
|
325
|
+
@serial_tty_error = '/dev/ttyUSB3'
|
326
|
+
@serial_tty = '/dev/ttyUSB2'
|
327
|
+
@ussd_add = (lsusb =~ /12d1:14ac/) ? ',15' : ''
|
328
|
+
@serial_eats_sms = true
|
329
|
+
when /airtel-modem/
|
330
|
+
log_msg :SerialModem, 'Found 3G-modem with ttyUSB0-ttyUSB4'
|
331
|
+
@serial_tty_error = '/dev/ttyUSB5'
|
332
|
+
@serial_tty = '/dev/ttyUSB4'
|
333
|
+
@ussd_add = ''
|
334
|
+
else
|
335
|
+
#puts caller.join("\n")
|
336
|
+
@serial_tty = @serial_tty_error = nil
|
337
|
+
end
|
338
|
+
dputs(2){"serial_tty is #{@serial_tty.inspect} and exists " +
|
339
|
+
"#{File.exists?(@serial_tty.to_s)}"}
|
340
|
+
if @serial_tty_error && File.exists?(@serial_tty_error)
|
341
|
+
log_msg :SerialModem, 'resetting modem'
|
342
|
+
reload_option
|
343
|
+
end
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
def start_serial_thread
|
348
|
+
@serial_thread = Thread.new {
|
349
|
+
#dputs_func
|
350
|
+
dputs(2){'Thread started'}
|
351
|
+
loop {
|
352
|
+
begin
|
353
|
+
dputs(5) { 'Reading out modem' }
|
354
|
+
if read_reply.length == 0
|
355
|
+
#@serial_sms.each { |id, sms|
|
356
|
+
# ddputs(3) { "Deleting sms #{sms.inspect} afterwards" }
|
357
|
+
# sms_delete(id)
|
358
|
+
#}
|
359
|
+
end
|
360
|
+
|
361
|
+
dputs(4) { (Time.now - @serial_ussd_last).to_s }
|
362
|
+
if (Time.now - @serial_ussd_last > @serial_ussd_timeout) &&
|
363
|
+
(@serial_ussd.length > 0)
|
364
|
+
log_msg :SerialModem, "Re-sending #{@serial_ussd.first}"
|
365
|
+
ussd_send_now
|
366
|
+
end
|
367
|
+
|
368
|
+
sms_scan
|
369
|
+
|
370
|
+
sleep 0.5
|
371
|
+
rescue IOError
|
372
|
+
log_msg :SerialModem, 'IOError - killing modem'
|
373
|
+
kill
|
374
|
+
return
|
375
|
+
rescue Exception => e
|
376
|
+
dputs(0) { "#{e.inspect}" }
|
377
|
+
dputs(0) { "#{e.to_s}" }
|
378
|
+
e.backtrace.each { |l| dputs(0) { l } }
|
379
|
+
end
|
380
|
+
dputs(5) { 'Finished' }
|
381
|
+
}
|
382
|
+
dputs(1) { 'Finished thread' }
|
383
|
+
}
|
384
|
+
end
|
385
|
+
|
386
|
+
def reload_option
|
387
|
+
@serial_sp and @serial_sp.close
|
388
|
+
@serial_sp = nil
|
389
|
+
dputs(1) { 'Trying to reload modem-driver - killing and reloading' }
|
390
|
+
%w( chat ppp).each { |pro|
|
391
|
+
System.run_str("killall -9 #{pro}")
|
392
|
+
}
|
393
|
+
%w(rmmod modprobe).each { |cmd|
|
394
|
+
System.run_str("#{cmd} option")
|
395
|
+
}
|
396
|
+
end
|
397
|
+
|
398
|
+
def kill
|
399
|
+
#dputs_func
|
400
|
+
if @serial_thread
|
401
|
+
if @serial_thread.alive?
|
402
|
+
dputs(3) { 'Killing thread' }
|
403
|
+
@serial_thread.kill
|
404
|
+
dputs(3) { 'Joining thread' }
|
405
|
+
@serial_thread.join
|
406
|
+
dputs(3) { 'Thread joined' }
|
407
|
+
end
|
408
|
+
end
|
409
|
+
@serial_sp and @serial_sp.close
|
410
|
+
dputs(1) { 'SerialModem killed' }
|
411
|
+
@serial_sp = nil
|
412
|
+
end
|
413
|
+
|
414
|
+
def attached?
|
415
|
+
@serial_sp != nil
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'serial_modem'
|
7
|
+
spec.version = '0.3.0'
|
8
|
+
spec.authors = ['Linus Gasser']
|
9
|
+
spec.email = ['ineiti@linusetviviane.ch']
|
10
|
+
spec.summary = %q{Interface to serial-usb-modems}
|
11
|
+
spec.description = %q{This can interface a lot of different usb-modems and serial modems}
|
12
|
+
spec.homepage = 'https://github.com/ineiti/SerialModem'
|
13
|
+
spec.license = 'GPLv3'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_runtime_dependency 'serialport', '1.3.1'
|
23
|
+
end
|
data/test/run_test
ADDED
data/test/test_hw.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/bin/env ruby
|
2
|
+
#$LOAD_PATH.push '../lib'
|
3
|
+
DEBUG_LVL=5
|
4
|
+
|
5
|
+
require 'serial_modem'
|
6
|
+
include SerialModem
|
7
|
+
|
8
|
+
def main
|
9
|
+
test_send_ussd
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_remove
|
13
|
+
setup_modem
|
14
|
+
check_presence
|
15
|
+
sleep 3
|
16
|
+
kill
|
17
|
+
sleep 5
|
18
|
+
reload_option
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_send_ussd
|
22
|
+
setup_modem
|
23
|
+
#check_presence
|
24
|
+
ussd_send('*100#')
|
25
|
+
sleep 5
|
26
|
+
#dp 'done'
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_old
|
30
|
+
setup_modem nil
|
31
|
+
check_presence
|
32
|
+
#set_connection_type '2go'
|
33
|
+
#sleep 10
|
34
|
+
ussd_send('*128#')
|
35
|
+
sleep 5
|
36
|
+
#set_connection_type '3g'
|
37
|
+
#sleep 10
|
38
|
+
#ussd_send('*128#')
|
39
|
+
#sleep 10
|
40
|
+
#ussd_send('*128#')
|
41
|
+
#sleep 10
|
42
|
+
#sleep 1
|
43
|
+
#ussd_send('*128#')
|
44
|
+
#sms_send('93999699', 'SMS from Dreamplug')
|
45
|
+
#sms_send('100', 'internet')
|
46
|
+
#sms_scan
|
47
|
+
#sleep 10
|
48
|
+
#ussd_send('*100#')
|
49
|
+
#sms_scan
|
50
|
+
#@huawei_sms.each{|k,v| puts "#{k}: #{v.inspect}"}
|
51
|
+
#sms_scan
|
52
|
+
#sms_delete( 0 )
|
53
|
+
#sms_scan
|
54
|
+
#sleep 10
|
55
|
+
#puts SerialModem::send_modem('atz')
|
56
|
+
#sleep 10
|
57
|
+
end
|
58
|
+
|
59
|
+
main
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: serial_modem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Linus Gasser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: serialport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.3.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.1
|
55
|
+
description: This can interface a lot of different usb-modems and serial modems
|
56
|
+
email:
|
57
|
+
- ineiti@linusetviviane.ch
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- commit
|
68
|
+
- lib/serial_modem.rb
|
69
|
+
- serial_modem.gemspec
|
70
|
+
- test/run_test
|
71
|
+
- test/test_hw.rb
|
72
|
+
homepage: https://github.com/ineiti/SerialModem
|
73
|
+
licenses:
|
74
|
+
- GPLv3
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.2.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Interface to serial-usb-modems
|
96
|
+
test_files:
|
97
|
+
- test/run_test
|
98
|
+
- test/test_hw.rb
|