📜 ⬆️ ⬇️

Easy management of your Arduino via the web

This article is intended for beginners. Here it will be described how from a web application using ajax requests to send phyton commands to a script that will send them via the serial port directly to our arduino.
You bought yourself an Arduino, tried a few examples, played around with sketches. But this is not enough for you, you want to control, manage all this good through the Internet. The easiest way is to purchase a shield with an Ethernet port and connect it to the Arduino (or purchase a scarf with an already built-in Ethernet). But it costs more, and in management it is necessary to push it.



For the work we need:
- HTTP server
- python interpreter
- Arduino
Here I will describe where to get the first and second, and how to make friends
Now in order. As an HTTP server, I use Apache. Install it is not difficult. If you are completely new and use windows, you can take the Denwer package from the official site, it has Apache.
Python (I used version 3.3) can also be taken from the official site and installed. Now we need to make friends with our Apache and python. The easiest way is to run python as cgi. To do this, open the httpd.conf file in the conf folder in the place where you set your apache (if you set denwer, the path will be something like the following: [virtual disk letter]: \ usr \ local \ bin \ apache)
')
Looking for a line

AddHandler cgi-script .cgi

Add a .py at the end with a space and see that there is no # sign at the beginning of the line. Save, restart the server.
Now to test the close friendship between pythone and apache, you can create a test file and put it in your home folder.
#!/Python33/python.exe print ("STATUS: 200 OK\n\n") print ("<b>hello world</b>") 

Please note that the first line we show where we have an interpreter of the language. I, for example, have it at C: /Python33/python.exe. I think you figure it out. Call it what you want and access it through a browser, for example, localhost / my_first_test_phyton_file.py. If you see "hello world", then all is well.


The code of the main JavaScript control script is extremely simple:
 //    Arduino var serialPort = 'COM5'; //   var Arduino = function(command, callback){ $.get('c.py',{ c:command, p:serialPort }, callback); } 


The only thing that needs to be changed here, as you guessed it, is the port on which you have an arduino connected. It can always be viewed in windows using the Device Manager. We will send it to our python script so that it knows on which serial port to send the received data.
Now, if we make a call to our function, for example: Arduino (123), the script will create an ajax request of the form c.py? C = 123 & p = COM5 and send it to our python script c.py. Consider what he is:
 #!/Python33/python.exe import serial import cgi print ("STATUS: 200 OK\n") req = cgi.FieldStorage(); ser = serial.Serial(req['p'].value, 9600, timeout=1) ser.write(bytes(req['c'].value,'latin')) ser.close() print ("ok") 

In fact, it simply takes the value of the parameter "c", sends it to the serial port "p" and writes "ok". Cheap and angry.
For those who want not only to give, but also to receive, we will write more code
Let's a little improve our client part.
 //   var Arduino = function(sp, errorCallback) { this.serialPort = sp; this.errorCallback = errorCallback || function(){ console.log('Error'); } this.send = function(data, callback){ var callback = callback; var self = this; data['p'] = this.serialPort; data['s'] = Math.round(Math.random()*1000); //  ,     $.ajax({ url:'c.py', data:data, success:function(data){ if($.trim(data) == 'error'){ self.errorCallback(); } else { if(typeof callback == "function") callback(data); } } }); } // this.set = function(command, callback){ this.send({ c:command, r:0 }, callback); } //    this.get = function(command, callback){ this.send({ c:command, r:1 //    " " }, callback); } } 

Now, since we have turned Arduino into a class, the simplest call would be something like this:
 var myArduino = new Arduino('COM5'); myArduino.set(113); //    13 myArduino.get(36,function(data){console.log(data)}); //   6.      

And, of course, you need to slightly change the server part:
 #!/Python33/python.exe import serial import cgi print ("STATUS: 200 OK\n") req = cgi.FieldStorage(); try: ser = serial.Serial(req['p'].value, 9600, timeout=1) except: print("error") exit() ser.write(bytes(req['c'].value,'latin')) if int(req['r'].value) == 1: res = ''; while not res: res = ser.readline() print(res.decode('UTF-8')) else: print ("ok") ser.close() 

Here, almost nothing has changed, except that when the server in the request receives the parameter r = 1, then it expects a response from Arduino.
And we added a check to see if our script could open the serial port. If not, will return the keyword "error"

Now let's consider a sketch for arduino, which accepts and processes all this:
 #include <Servo.h> Servo myservo; void setup() { Serial.begin(9600); } String getParam(){ String re; while (Serial.available()) { re.concat(Serial.read()-48); } return re; } int getPin(String p){ return p.substring(0,2).toInt(); } int getVal(String p){ return p.substring(2,6).toInt(); } //   void loop() { while (Serial.available()) { char command = (char)Serial.read(); String param = getParam(); int pin = getPin(param); int p; switch (command) { case '0': //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, LOW); break; case '1': //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, HIGH); break; case '2': //Servo myservo.attach(pin); p = getVal(param); myservo.write(p); break; case '3': //Digital read pinMode(pin,INPUT); Serial.print(digitalRead(pin)); break; case '4': { //Analog read int aPin = A0; switch (pin) { case 1: aPin = A1; break; case 2: aPin = A2; break; case 3: aPin = A3; break; case 4: aPin = A4; break; case 5: aPin = A5; break; } Serial.print(analogRead(aPin)); } break; case '5': //Analog write pinMode(pin,OUTPUT); p = getVal(param); analogWrite(pin, p); break; } } } 

By serial port we will send commands of the form: 1234567 where:
[1] - team number
[23] - pin number
[4567] - data for pin, if necessary.
For example:
113 - sets pin 13 to the pin and transfers the HIGH status (that is, turns it on) over it.
013 - sets pin 13 to the pin and transmits the LOW state (that is, turns it off) over it.
209100 - sets pin 9 as servo control and transmits it a value of 100 through PWM modulation.
310 - sets pin 10 to input and reads HIGH / LOW data from it and returns as 1 or 0, respectively.
You can easily add your own commands in the switch case block.
Now add some beauty to our frontend part and get, for example,

Next, I added some user interface magic . But I will not describe it, all interested can take it from the archive with the project .
For the web part I used Bootstrap (solely because of the convenience and its “rubberiness”) and jQuery (for ajax).
Now let's see how it works.
First you need to specify which port your device has and how many pins it has. Then choose on which pin you have what is and go on to the management.

Among the disadvantages of this approach, we can note the relatively slow data exchange rate. To find out the status, for example, the buttons must be sent requests, but too often it can not be done, because we can rest against a busy serial port. On web sockets would work faster, but this is a slightly more advanced topic, which I, if you want, will cover later.
Everything was checked under Windows 8 x64. Probably, there are some features of the implementation of all this under other systems, I will be glad to hear about it in the comments.
Now about where all this can come in handy: for example, you can make a demonstration stand; control camera position; connect the temperature sensor and other devices and remotely monitor some process, etc.

Archive with the project
To run on the iPad in full screen, I used the free program oneUrl

In thematic hubs not inserted just because of the lack of karma.
This is my first article. I will be glad to answer questions.

UPD: At the request of the workers, I tested this method on MacOS as well. There were no particular problems. On a Mac, python is usually already default, the only thing to do is to make friends with apache. The first line in c.py will be
#!/usr/bin/python
Also, maybe you will not have pyserial python extension installed, it is installed by a simple command in the console:
easy_install -U pyserial
Further it should be noted that usually the pre-installed version of python is quite old and the string may not work.
 ser.write(bytes(req['c'].value,'latin')) 

I replaced it with
 ser.write(bytes(req['c'].value.decode('latin'))) 

It all worked.
Do not forget to look at which port your device is connected to. This is convenient to watch for example through the Arduino program itself. Menu Service-> Serial Port. For example, he looked like this: /dev/cu.usbmodemfd141
I wish you all successful experiences.

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


All Articles