uLisp GSM server
This application describes a remote uLisp server, running on an Arduino. You can send Lisp functions to it in standard mobile phone text messages (SMS), and the uLisp server will reply with the answer:
The uLisp Server uses uLisp running on an Arduino Mega 2560 linked to an Adafruit Fona GSM board . It connects to the Arduino Mega 2560 using the TX1 and RX1 lines on serial port 1.
All the communication with the GSM board is written in uLisp, using the serial input/output functions read-line and write-line, and the string functions princ-to-string and read-from-string, introduced in uLisp version 1.9.
For example, if you wanted to know 12*34*56 you could send it the text message:
(* 12 34 56)
and you would receive the reply:
More seriously, if you had sensors, such as thermometers, and output devices, such as LEDs, connected to the Arduino you could query the sensors and control the output devices remotely. For example, to read a thermometer on analogue input 1:
or to turn on an LED on pin 10:
(digitalwrite 10 t)
But not only that; you can also write and run programs remotely. For example, suppose you want to turn off all the LEDs on pins 0 to 9 you could define a new function off by sending the message:
(defun off (x) (dotimes (p x) (digitalwrite p nil)))
and then run it by sending the message:
First the program defines some AT command strings used to communicate with the GSM module :
(defvar ok (concatenate 'string "OK" (string #\return))) (defvar at-textmode "AT+CMGF=1") (defvar at-usesim "AT+CPMS=\"SM\"") (defvar at-send "AT+CMGS=\"") (defvar at-readall "AT+CMGL=\"ALL\"") (defvar at-delete "AT+CMGD=")
The function command sends an AT command to the phone, and waits for the "OK" reply:
(defun command (message) (with-serial (str 1) (write-line message str) (loop (let ((line (read-line str))) (print line) (when (string= line ok) (return))))))
The function get-sms reads all the sms messages on the phone, and returns them as a list consisting of:
(n phone message)
for each message, where n is the number of the message, phone is the phone number of the sender (as a string), and message is the text of the message:
(defun get-sms () (let (all) (with-serial (str 1) (write-line at-readall str) (read-line str) (loop (let ((head (read-line str))) (when (string= head ok) (return)) (let* ((message (read-line str)) (c (commas head)) (nstr (subseq head 6 (first c))) (n (read-from-string nstr)) (phone (read-from-string (subseq head (1+ (second c)) (third c))))) (push (list n phone message) all) (read-line str))))) all))
This uses the following function commas, which returns a list of the positions of the commas in a string:
(defun commas (string) (let (commas) (dotimes (x (length string) (reverse commas)) (when (eq (char string x) #\,) (push x commas)))))
The function send sends an SMS message to a specified phone number (as a string):
(defun send (phone message) (with-serial (str 1) (write-line (concatenate 'string at-send phone "\"") str) (write-string message str) (write-byte 26 str)))
The function delete deletes a specified message:
(defun delete (n) (command (concatenate 'string at-delete (princ-to-string n))))
Finally, listener waits in a loop for incoming messages, evaluates them, and sends back the result:
(defun listener () (command at-textmode) (command at-usesim) (loop (let ((check (get-sms))) (when check (print check) (let* ((sms (first check)) (n (first sms)) (phone (second sms)) (message (third sms)) (result (eval (read-from-string message))) (reply (princ-to-string result))) (print "Replying to ") (princ phone) (send phone reply) (delay 5000) (delete n)))) (delay 5000)))
A practical version of this server should include error checking, and some security to prevent unauthorised access.
Here's the whole listing: uLisp GSM server program.