DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Send And Receive SMS Text Messages With Ruby And A GSM/GPRS Modem

03.08.2007
| 32124 views |
  • submit to reddit
        
require 'serialport'
require 'time'

class GSM
  
  SMSC = "+447785016005"  # SMSC for Vodafone UK - change for other networks

  def initialize(options = {})
    @port = SerialPort.new(options[:port] || 3, options[:baud] || 38400, options[:bits] || 8, options[:stop] || 1, SerialPort::NONE)
    @debug = options[:debug]
    cmd("AT")
    # Set to text mode
    cmd("AT+CMGF=1")
    # Set SMSC number
    cmd("AT+CSCA=\"#{SMSC}\"")    
  end
  
  def close
    @port.close
  end
  
  def cmd(cmd)
    @port.write(cmd + "\r")
    wait
  end
  
  def wait
    buffer = ''
    while IO.select([@port], [], [], 0.25)
      chr = @port.getc.chr;
      print chr if @debug == true
      buffer += chr
    end
    buffer
  end

  def send_sms(options)
    cmd("AT+CMGS=\"#{options[:number]}\"")
    cmd("#{options[:message][0..140]}#{26.chr}\r\r")
    sleep 3
    wait
    cmd("AT")
  end
  
  class SMS
    attr_accessor :id, :sender, :message, :connection
    attr_writer :time
    
    def initialize(params)
        @id = params[:id]; @sender = params[:sender]; @time = params[:time]; @message = params[:message]; @connection = params[:connection]
    end
    
    def delete
      @connection.cmd("AT+CMGD=#{@id}")
    end
    
    def time
      # This MAY need to be changed for non-UK situations, I'm not sure
      # how standardized SMS timestamps are..
      Time.parse(@time.sub(/(\d+)\D+(\d+)\D+(\d+)/, '\2/\3/20\1'))
    end
  end
  
  def messages
    sms = cmd("AT+CMGL=\"ALL\"")
    # Ugly, ugly, ugly!
    msgs = sms.scan(/\+CMGL\:\s*?(\d+)\,.*?\,\"(.+?)\"\,.*?\,\"(.+?)\".*?\n(.*)/)
    return nil unless msgs
    msgs.collect!{ |m| GSM::SMS.new(:connection => self, :id => m[0], :sender => m[1], :time => m[2], :message => m[3].chomp) } rescue nil
  end
end


destination_number = "+44 someone else"

p = GSM.new(:debug => false)

# Send a text message
p.send_sms(:number => destination_number, :message => "Test at #{Time.now}")

# Read text messages from phone
p.messages.each do |msg|
  puts "#{msg.id} - #{msg.time} - #{msg.sender} - #{msg.message}"
  # msg.delete
end
    

Comments

Snippets Manager replied on Mon, 2008/09/29 - 3:17pm

How do I strip the line numbers from the code. Why aren't they separated from the source :(

Snippets Manager replied on Sun, 2007/08/26 - 4:23pm

sorry for the ignorance. class GSM is just plain Model class with SMS class defined inside it? Or should this code be in a controller? Thanks.

Snippets Manager replied on Mon, 2012/05/07 - 2:36pm

http://raa.ruby-lang.org/project/ruby-serialport/ as far as I recall. I had to make a tweak to its C code to deal with my GSM modem on OS X as it was on /dev/somethingweird rather than /dev/ttyS0 but it was an easy tweak.

Snippets Manager replied on Tue, 2007/03/20 - 1:17am

Where can I get the 'serialport' library? Please advice!