📜 ⬆️ ⬇️

Budget SMS

Greetings to all the farmers!

Of course, the licked topic about sending SMS messages, but as they say: "a lot - not a little." Somehow it happened that it was she who constantly persecutes me: then some other kind people will be asked to take part (by advice, for example) in the implementation of the budget distribution of messages. And therefore, in order not to dissolve the accumulated good, I will leave it here, but what if someone comes in handy ...

So, with ... We omit all the options for implementation on the basis of the usual computer and the axis of the NT family. And we proceed immediately to the "autonomous" systems.

What boasts arduino in this direction? I will answer right away, IT works, but there are nuances about which I will write below. In general, we have the Chinese version of the arduino 2560 (almost the entire line has been tried) and two additional modules - the W5100 network (the most stable version) and GSM SIM 900. It looks like this whole thing somehow.
')
image

The task was as follows:
- the device must be able to communicate on http
- send message
- output the result in json format

Google shares all the necessary information, and at the output we get the following code:

Sketch
#include <SPI.h> #include <Ethernet.h> #include <String.h> #include "SIM900.h" #include <SoftwareSerial.h> #include "sms.h" #include <LiquidCrystal_I2C.h> #include <Wire.h> byte mac[] = { 0x90, 0xA2, 0x00, 0x00, 0x00, 0x01 }; IPAddress ip(192,168,34,139); EthernetServer server(80); char char_in = 0; String HTTP_req; SMSGSM sms; boolean started=false; bool power = false; LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); void setup() { Serial.begin(9600); lcd.begin(16,2); lcd.setCursor(0,0); lcd.print("INIT GSM..."); lcd.setCursor(0,1); lcd.print("WAIT!!!"); //powerUp(); gsm.forceON(); if (gsm.begin(4800)) { Serial.println("\nstatus=READY"); lcd.clear(); lcd.setCursor(0,0); lcd.print("READY"); started=true; } else { Serial.println("\nstatus=IDLE"); lcd.clear(); lcd.setCursor(0,0); lcd.print("IDLE"); } Ethernet.begin(mac, ip); server.begin(); } void software_reset() { asm volatile (" jmp 0"); } void loop() { EthernetClient client = server.available(); if (client) { while (client.connected()) { if (client.available()) { char_in = client.read(); // HTTP_req += char_in; if (char_in == '\n') { Serial.println(HTTP_req); if(HTTP_req.indexOf("GET /res") >= 0) { reset_processing(&HTTP_req, &client); break; } if(HTTP_req.indexOf("GET /sms") >= 0) { sms_processing(&HTTP_req, &client); break; } if(HTTP_req.indexOf("GET /test") >= 0) { test_processing(&HTTP_req, &client); break; } else { client_header(&client); break; } } } } HTTP_req = ""; client.stop(); } if(power) { delay(1000); software_reset(); } } char* string2char(String command) { if(command.length()!=0){ char *p = const_cast<char*>(command.c_str()); return p; } } void parse_data(String *data) { data->replace("GET /sms/",""); data->replace("GET /test/", ""); int lastPost = data->indexOf("\r"); *data = data->substring(0, lastPost); data->replace(" HTTP/1.1", ""); data->replace(" HTTP/1.0", ""); data->trim(); } // explode String request_value(String *data, char separator, int index) { int found = 0; int strIndex[] = {0, -1}; int maxIndex = data->length()-1; for(int i=0; i<=maxIndex && found<=index; i++) { if(data->charAt(i)==separator || i==maxIndex) { found++; strIndex[0] = strIndex[1]+1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found>index ? data->substring(strIndex[0], strIndex[1]) : ""; } bool gsm_status() { bool result = false; switch(gsm.CheckRegistration()) { case 1: result = true; break; default: break; } return result; } bool gsm_send(char *number_str, char *message_str) { bool result = false; switch(sms.SendSMS(number_str, message_str)) { case 1: result = true; break; default: break; } return result; } void reset_processing(String *data, EthernetClient *cl) { client_header(cl); cl->println("\{\"error\": 0, \"message\": \"restarting...\"\}"); power = true; } void test_processing(String *data, EthernetClient *cl) { parse_data(data); if(started) { client_header(cl); cl->println("\{\"id\":" + request_value(data, '/',0) + ",\"error\":0" + ",\"message\":\"test success\"\}"); } } void sms_processing(String *data, EthernetClient *cl) { parse_data(data); if(started) { if (gsm_send(string2char(request_value(data, '/', 1)), string2char(request_value(data, '/', 2)))) { client_header(cl); cl->println("\{\"id\":" + request_value(data, '/',0) + ",\"error\":0" + ",\"message\":\"success\"\}"); } else { if(!gsm_status()) { client_header(cl); cl->println("\{\"id\":" + request_value(data, '/',0) + ",\"error\":2" + ",\"message\":\"gsm not registered\"\}"); power = true; } else { client_header(cl); cl->println("\{\"id\":" + request_value(data, '/',0) + ",\"error\":1" + ",\"message\":\"fail\"\}"); } } } } void client_header(EthernetClient *cl) { cl->println("HTTP/1.1 200 OK"); cl->println("Content-Type: text/plain"); cl->println("Connection: close"); cl->println(); } 



Fill, packaged in a box. It seems to be beautiful, we give to good people.

image

What happened in the code:
- raised a simple http
- we process simple GET
- we send the received data via SERIAL to SIM 900
- we answer with the help of “JSON”

And here there is one big nuance; smart people have a task to implement some service in order to learn how to send a packet of messages through this device right away, but this is not my problem. In production, the device behaved satisfactorily.

We are increasing capacity ... The task is completely analogous: repetition is the mother of learning. Smart people have already created a cool service to work with the previous device: turn, history and other usefulness.

So, we have a raspberry pi on hand, the same SIM 900 module (it was taken only for experiments, because Linux works fine with 3g-modems via USB) and the 3g-modem huawei e-line itself

image

Again we ask Google the right questions, read the results, determine the implementation language - python - quickly, simply, reliably ...

script
 import serial, time from flask import Flask import RPi.GPIO as GPIO app = Flask(__name__) def sim900_on(): gsm = serial.Serial('/dev/ttyAMA0', 115200, timeout=1) gsm.write('ATZ\r') time.sleep(0.05) abort_after = 5 start = time.time() output = "" while True: output = output + gsm.readline() if 'OK' in output: gsm.close() return True delta = time.time() - start if delta >= abort_after: gsm.close() break #GPIO.setwarnings(False) GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.output(11, True) time.sleep(1.2) GPIO.output(11, False) return False def gsm_send(id, port, phone, msg): delay = False if 'AMA' in port: delay = True msg = msg.replace('\\n', '\n') msg = msg.replace('\s', ' ') gsm = serial.Serial('/dev/tty%s' % port, 115200, timeout=1) gsm.write('ATZ\r') if delay: time.sleep(0.05) gsm.write('AT+CMGF=1\r\n') if delay: time.sleep(0.05) gsm.write('AT+CMGS="%s"\r\n' % phone) if delay: time.sleep(0.05) gsm.write(msg + '\r\n') if delay: time.sleep(0.05) gsm.write(chr(26)) if delay: time.sleep(0.05) abort_after = 15 start = time.time() output = "" while True: output = output + gsm.readline() #print output if '+CMGS:' in output: print output gsm.close() return '{"id":%s,"error":0,"message":"success", "raw":"%s"}' % (id, output) if 'ERROR' in output: print output gsm.close() return '{"id":%s,"error":0,"message":"fail", "raw":"%s"}' % (id, output) delta = time.time() - start if delta >= abort_after: gsm.close() return '{"id":%s,"error":1,"message":"timeout", "raw":"%s"}' % (id, output) @app.route('/sms/<id>/<port>/<phone>/<msg>',methods=['GET']) def get_data(id, port, phone, msg): return gsm_send(id, port, phone, msg) @app.route('/',methods=['GET']) def index(): return "Hello World" if __name__ == "__main__": sim900_on() app.run(host="0.0.0.0", port=8080, threaded=True) 



We feed the python, launch it using the start-stop-daemon, give it a friendly look, give it to kind people ...

image

It turned out almost one to one, only due to the USB bus system can be expanded. There were no complaints at all in the production process - everyone was VERY pleased.

The device turned out so successful that there was a desire to use this business in "personal" interests, namely to introduce this device into the monitoring system. But it was necessary to get rid of the main nuance - the lack of a message queue. I took the principle of implementation from one well-known vendor (he proposed a software and hardware complex, part of which raised an smtp server for processing notifications and sending it to a gsm device). Such a scheme is embedded in any monitoring system.

So, the necessary knowledge has long been obtained, proceed to implement.

SMTP daemon
 #!/usr/bin/env python2.7 # -*- coding: utf-8 -*- import smtpd import asyncore import email import MySQLdb import subprocess def InsertNewMessage(phone, msg): conn = MySQLdb.connect(host="localhost", # your host, usually localhost user="sms", # your username passwd="sms", # your password db="sms") # name of the data base c = conn.cursor() c.execute('insert into message_queue (phone, message) values ("%s", "%s")' % (phone, msg)) conn.commit() conn.close() class CustomSMTPServer(smtpd.SMTPServer): def process_message(self, peer, mailfrom, rcpttos, data): msg = email.message_from_string(data) phone = rcpttos[0].split('@',1)[0] addr = mailfrom for part in msg.walk(): if part.get_content_type() == "text/plain": # ignore attachments/html body = part.get_payload(decode=True) InsertNewMessage(phone, str(body)) subprocess.Popen("/home/pi/daemons/sms/pygsmd.py", shell=True) server = CustomSMTPServer(('0.0.0.0', 25), None) asyncore.loop() 



Demonization occurs, as I wrote above, with the help of “start-stop-daemon”, and the smtp script itself starts a subprocess for working with the message base.

gsm script
 #!/usr/bin/env python2.7 # -*- coding: utf-8 -*- import serial import time import MySQLdb import commands def gsm_send(port, phone, msg): print 'Sending message: %s to: %s' % (msg, phone) gsm = serial.Serial('/dev/tty%s' % port, 460800, timeout=5, xonxoff = False, rtscts = False, bytesize = serial.EIGHTBITS, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE ) gsm.write('ATZ\r\n') time.sleep(0.05) gsm.write('AT+CMGF=1\r\n') time.sleep(0.05) gsm.write('''AT+CMGS="''' + phone + '''"\r''') time.sleep(0.05) gsm.write(msg + '\r\n') time.sleep(0.05) gsm.write(chr(26)) time.sleep(0.05) abort_after = 15 start = time.time() output = "" while True: output = output + gsm.readline() #print output if '+CMGS:' in output: #print output gsm.close() return 0 if 'ERROR' in output: #print output gsm.close() return 1 delta = time.time() - start if delta >= abort_after: gsm.close() return 1 def msg_delete(list): conn = MySQLdb.connect(host="localhost", user="sms", passwd="sms", db="sms") c = conn.cursor() c.execute("delete from message_queue where id in %s;" % list) conn.commit() conn.close() def msg_hadle(): list = tuple() conn = MySQLdb.connect(host="localhost", user="sms", passwd="sms", db="sms") c = conn.cursor() c.execute("select * from message_queue") numrows = int(c.rowcount) if numrows > 0: for row in c.fetchall(): result = gsm_send('USB0', ('+' + row[1]), row[2]) if result == 0: list +=(str(row[0]),) conn.close() if len(list) == 1: qlist = str(list).replace(',','') if len(list) > 1: qlist = str(list) if len(list) > 0: msg_delete(qlist) del list while True: try: msg_hadle() except: print "mysql error" time.sleep(10) 



In conjunction with my monitoring system, the device behaves adequately, although it works not so long ago. I hope the material will be useful to someone.

Source: https://habr.com/ru/post/261387/


All Articles