UDP - TCP - Arduino

udp tcp arduino

ArduNetstD — программа для обмена данными с ардуиной и другими девайсами.





Началось всё с того, что была написана программа для удалённого управления ардуиной по протоколу UDP

Протокол TCP, по которому ваш браузер получает данные, требует установления соединения происходящего в три этапа — установка соединения, передача данных и завершение соединения. При использовании udp-протокола, клиент не «договаривается» с сервером, а просто отправляет ему пакет не заботясь о том, доставлен он или нет. UDP-сервер получая пакет может ответить или не отвечать, в зависимости от настроек.

… следом появилась идея сделать программу (по типу ser2net), способную гонять данные туда-сюда используя разные протоколы.
В итоге получился прокси-сервер ardunetstd, который умеет вот что…

• Работает по протоколам UDP и TCP.

• Перегоняет данные «как есть» в ардуину и обратно.

• Предоставляет WEB-интерфейс для управления ардуиной.

• Даёт возможность обмениваться данными с ардуиной при помощи программ типа curl.

• Размер файла 8кБ (при работе на роутере флешка не требуется).


Подключать можно не только ардуину, но и другие USB-устройства, например, я подключал GPS-приёмник BU-353.

Никаких сторонних программ для настройки порта не нужно.




Как пользовать


Скачать ardunetstd (пока не скачивайте)

Для компьютера x64

Для Atheros (MR3020)

Для Broadcom (Asus wl-500)


Если нужно для иного роутера, тогда пишите в комментах.



Запускается программа с параметрами, которые отвечают за режим работы:

./ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 0 0 8080 /opt/index.web 1 100000

Двенадцать параметров разделённых пробелом.


1. Путь до ардуины.

2. Скорость порта (9600, 19200, 38400, 57600, 115200).

3. Файл в который записываются данные принятые от ардуины, он будет создаваться сам при каждом запуске программы, называться может как угодно. Находиться тоже может где угодно, однако лучше использовать папку /tmp (это временная папка, которую создаёт система в оперативной памяти).

4. Порт для upd-сервера (на этот порт приходят данные от клиента), можно указать какой-либо другой.

5. Порт для upd-клиента (на этот порт ardunetstd будет отправлять клиенту данные от ардуины), можно указать какой-либо другой.

6. Адрес udp-клиета, которому будут отправляться данные принятые от ардуины (писать в кавычках — «192.168.1.224»).

7. Запуск udp-сервера (0 — не запускать, 1 — запускать).

8. Запуск udp-клиента (0 — не запускать, 1 — запускать).

9. Порт для web-сервера.

10. Путь к файлу index.web

11. Запуск web-сервера (0 — не запускать, 1 — запускать).

12. Пауза перед отправкой ответа на web-страничку (в микросекундах).


Для остановки программы нажмите Ctrl + c, если случайно нажали другое сочетание клавиш, тогда сделайте:

killall -s 9 ardunetstd




Теперь подробнее обо всём…




WEB


Начну с конца, точнее с web-сервера. Предположим Вы хотите поуправлять ардуиной (подключённой к роутеру или обычному компьютеру) или просто передать её что-то через веб-страничку. Для этого нужно положить ardunetstd в любую папку, допустим в /opt

Создайте папку, загрузите ardunetstd и дайте права:

mkdir /opt
cd /opt
wget ...
chmod -R 777 /opt


Далее надо в той же папке создать файл с html-кодом, который ardunetstd будет отдавать клиенту. Это такой же index.html-файл (как и в обычных серверах), только назвать его можно как угодно, у меня он будет называться index.web.



Создайте в папке /opt файл index.web с таким содержимым:
Если создавать влом, то можно его скачать...
cd /opt

wget //istarik.ru/file/index.web



<!DOCTYPE html><html><head>

<title>ArduNet stD</title>
<style>
body { background-color: #353535; color: #999; }
h1 { font-size: 60px; text-align: center; color: #353535; text-shadow: 0 0 2mm #ed9399}
</style></head>
<body><h1>ArduNet stD</h1>
<form action='' method='GET'>
<input type='text' name='To_Ardu' placeholder='To Ardu' size='50'>

<input id='submit' type='submit' value='Отправить'>
</form>


Получил от Ардуины: 
</body></html>


Если будете изменять файл index.web, то имейте в виду, он не должен превышать размер 2кБ.



Заливаем в ардуину код:


void setup() 
 {
   Serial.begin(57600);
   pinMode(13, OUTPUT);
   pinMode(12, OUTPUT);
 }
  
void loop() 
 {  
   char from_server; 
  
   while(Serial.available())
    {
       delay(1);
       from_server = Serial.read(); 

       // D13 
       if(from_server == 'A') 
         {
           digitalWrite(13, HIGH);
           Serial.print("D13 VKL");
           Serial.print('\n'); // это всегда должно быть везде
         }

       if(from_server == 'a') 
         {
           digitalWrite(13, LOW);
           Serial.print("D13 OTKL");
           Serial.print('\n');
         }

       // D12
       if(from_server == 'B') 
         {
           digitalWrite(12, HIGH);
           Serial.print("D12 VKL");
           Serial.print('\n');
         }

       if(from_server == 'b') 
         {
           digitalWrite(12, LOW);
           Serial.print("D12 OTKL");
           Serial.print('\n');
         }  
     
     } // конец while
      
 } // конец loop


Ардуина будет получать символ, выполнять действие и отправлять обратно статус. То есть отправляет данные только по запросу.


Строка, посылаемая ардуиной, должна обязательно заканчиваться \n



Далее предполагается, что ваша ардуина определилась как /dev/ttyUSB0
Про установку драйверов на роутер читать здесь.
Проверить можно так:

ls /dev/tty*




Если действия происходят на «большом компьютере», тогда стартуйте ardunetstd через sudo

sudo /opt/ardunetstd ...




Запускаем ardunetstd

Сделайте файл исполняемым.

sudo chmod +x /opt/ardunetstd


/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 0 0 8080 /opt/index.web 1 100000

В данном случае web-сервер запущен «1» на порту «8080», udp-сервер и udp-клиент отключены «0» «0».


Перейдя в браузере по адресу — адрес:8080, увидите картинку:




Впишите в поле символ А и нажмите «Отправить», на ардуине загорится D13 и появится ответ «D13 VKL».




В терминал будут выведены сообщения о проделанных действиях:



OK_GET — получен GET-запрос.
GET_send_toArdu: echo 'A' > /dev/ttyUSB0 — пришедший с GET-запросом символ 'A' отправлен ардуине.
Str_NOT_equallyоб этом ниже.
Reciv_from_arduino: D13 VKL | Bytes — 8 — от ардуины получена строка 'D13 VKL' размером 8 байт (восьмой байт это — \n).
Send to web_client: D13 VKL — клиенту отправлено 'D13 VKL'.



Как это работает?


Когда Вы открываете страничку, ardunetstd читает из файла index.web html-код и отдаёт его браузеру.
Когда отправляете символ, ardunetstd перенаправляет его в ардуину и становится на паузу в ожидании ответа. Длительность паузы указывается в последнем параметре (в данном случае 100000мкс = 100мс), она нужна для того, чтоб ардуина за это время успела обработать команду и вернуть ответ. Ардуина получает символ, выполняет действие и возвращает ответ (D13 VKL). Ardunetstd получает ответ и отправляет его клиенту, а так же помещает его во временный файл /tmp/tmp.txt (третий параметр).
Если данные принятые от ардуины не отличаются от тех, что содержатся в /tmp/tmp.txt, тогда их перезапись не производится (в терминале будет сообщение Str_equally), в противном случае данные перезапишутся, а терминал сообщит — Str_NOT_equally. Ниже будет объяснено назначение временного файла.




Curl — как пользоваться.

Откройте второй терминал и дайте команду:

curl localhost:8080/A

Вместо localhost впишите свой адрес.


Загорится D13 и во втором терминале будет выведена принятая от ардуины строка:



В первом терминале будут сообщения о проделанных действиях:




Если сделать пустой запрос:

curl localhost:8080




Тогда в ардуину отправится пустая строка и она ничего не ответит. Клиенту будет отправлена информация хранящаяся во временном файле /tmp/tmp.txt







WARNING!
Если планируется передача большого объёма данных, то нужно помнить о том, что у ардуины выделены довольно-таки скромные входящий (RX) и исходящий (TX) буферы, посмотреть и изменить их можно в файле /arduino-x.x.x/hardware/arduino/avr/cores/arduino/HardwareSerial.h

...
#if !defined(SERIAL_TX_BUFFER_SIZE)
#if (RAMEND < 1000)
#define SERIAL_TX_BUFFER_SIZE 16
#else
#define SERIAL_TX_BUFFER_SIZE 64 
#endif
#endif
#if !defined(SERIAL_RX_BUFFER_SIZE)
#if (RAMEND < 1000)
#define SERIAL_RX_BUFFER_SIZE 16
#else
#define SERIAL_RX_BUFFER_SIZE 64 
...

В данном случае для буферов выделено по 64 байта (16 байт — это для МК с объёмом RAM меньше 1кБ), то есть, если передать в ардуину больше 64-ёх символов и не успеть их прочитать, то они просто потеряются. Чтобы избежать потери, нужно увеличить буфер до нужного значения, например, если вы передаёте ардуине 100 символов, то буфер должен быть 102 байта.
Однако помните о том, что увеличивая буфер, вы увеличиваете объём занятой оперативной памяти.






Теперь изменим скетч таким образом, чтоб ардуина отправляла данные не по запросу, а делала это сама, с периодичностью в 250мс.


Предположим что Вы подключили к ардуине датчик температуры и хотите узнавать эту температуру не обращаясь к ардуине. То есть ардуина будет сама отправлять данные на сервер (ardunetstd), а сервер будет сохранять полученную инфу в файл /tmp/tmp.txt. Клиент, обращаясь к серверу будет забирать актуальные данные из /tmp/tmp.txt.



Остановите ardunetstd (Ctrl + c) и прошейте в ардуину код:


byte d13 = 0;
byte d12 = 0;

#define MAXMILLIS 4294967295
unsigned long timeobnv;
unsigned long timelapsed = 0;
int t = 22;


void setup() 
 {
   Serial.begin(57600);
   pinMode(13, OUTPUT);
   pinMode(12, OUTPUT);
 }
  
void loop() 
 {  
   char from_server; 
  
   while(Serial.available())
    {
       delay(1);
       from_server = Serial.read(); 

       // D13 
       if(from_server == 'A') 
         {
           digitalWrite(13, HIGH);
           d13 = 1;
         }

       if(from_server == 'a') 
         {
           digitalWrite(13, LOW);
           d13 = 0;
         }

       // D12
       if(from_server == 'B') 
         {
           digitalWrite(12, HIGH);
           d12 = 1;
         }

       if(from_server == 'b') 
         {
           digitalWrite(12, LOW);
           d12 = 0;
         }  
     
     } // конец while
     

   unsigned long currtime = millis(); 
 
   if(currtime > timeobnv) timelapsed = (currtime - timeobnv); 
 
   else timelapsed = (MAXMILLIS - timeobnv + currtime);

   if(timelapsed >= 250) 
    { 
      timeobnv = currtime;
      sendate();
    }
     
 } // конец loop


void sendate() // отправка данных
 {
   Serial.print("D13: "); 
   Serial.print(d13); 
   Serial.print("  D12: "); 
   Serial.print(d12); 
   Serial.print("  TEMP: "); 
   Serial.print(t); 
   Serial.print('\n'); // это всегда должно быть
 }

К прежнему функционалу добавлены счётчик времени и переменная с температурой.
Если D12 и D13 включены, то отсылает единички, наоборот, нолики.



Строка, посылаемая ардуиной, должна обязательно заканчиваться \n



Запустите ardunetstd изменив длительность паузы (последний параметр) до 350000 мкс. Пауза увеличена для того, чтоб в случае отправки ардуине команды, ardunetstd дождался ответа (у него в запасе будет 100мс).


/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 0 0 8080 /opt/index.web 1 350000


В терминале побегут строки:




Отправьте из веб-интерфейса (или curl`ом) символ A, D13 загорится и инфа изменится.




Замечу, если открыть соседний терминал и посмотреть потребляемые ресурсы (top), то будет видно, что ardunetstd берёт на себя не больше 2% CPU.


С web-сервером всё, перейдём к udp…




UDP



Потребуется UDP-клиент/сервер. Здесь (istarik.ru/file/ihome.apk) можно скачать простейшую программу для Android с интерфейсом и функциями схожими с web-интерфейсом описанным выше.

Позже выйдет статья с описанием более симпатичного и функционального приложения.



Нужно указать ip-адрес устройства, на котором работает ardunetstd и порты, после чего нажать кнопку «сохранить» и перезапустить приложение:






Теперь остановите ardunetstd (Ctrl + c), верните в ардуину первый скетч из статьи, и запустите ardunetstd с параметрами включающими приём и отправку udp-пакетов:

/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 1 1 8080 /opt/index.web 1 350000

Нужно указать ip-адрес вашего смартфона.
Если в последнем разряде ip-адреса указать цифру 255 (192.168.1.255), то пакеты будут отправляться всем udp-клиентам локальной сети (broadcast).

Android-приложение будет отправлять и принимать пакеты на порты 3495 и 3496.

Пауза, в работе udp ни на что не влияет.



На смартфоне введите символ A и нажмите отправить:


Сверху появится сообщение от ардуины.


Терминал расскажет о проделанной работе:



UDP_reciv — принят udp-пакет.
UDP_send_to_ardu: A — пришедший символ 'A' отправлен ардуине.
Str_NOT_equally — файл tmp.txt обновлён.
Reciv_from_arduino: D13 VKL | Bytes — 8 — от ардуины получена строка 'D13 VKL' размером 8 байт.
UDP_send: D13 VKL — клиенту отправлено 'D13 VKL'.



Если отправить через веб или с помощью curl символ a, то информация изменится на всех клиентах.




Если веб-сервер не нужен, то отключите его, поменяв предпоследний параметр с 1 на 0.

/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 1 1 8080 /opt/index.web 0 350000





Теперь остановите ardunetstd и вновь залейте в ардуину скетч, который сам отправляет данные.


Запустите ardunetstd с отключёнными web и udp серверами:

/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 0 1 8080 /opt/index.web 0 350000





В терминале побегут строки, а на смартфоне всегда будет отображаться актуальная информация.





Если нужно только отправлять ардуине информацию, тогда можно наоборот, отключить udp-клиент и включить udp-сервер:

/opt/ardunetstd /dev/ttyUSB0 57600 /tmp/tmp.txt 3495 3496 "192.168.1.224" 1 0 8080 /opt/index.web 0 350000



В общем можно комбинировать работу ardunetstd как угодно, в зависимости от потребностей.




Совместная работа



Ardunetstd можно использовать совместно с другим сервером (uhttpd, lighttpd и т.д.).

Предположим у вас есть красивый сайт (работающий не важно на каком движке, писаный не важно на каком языке и получающий контент не важно от какого сервера) и вы хотите с помощью этого сайта управлять своим «умным домом».
Чтобы реализовать эту идею достаточно в ваш html-код добавить ajax-запрос, который будет через ardunetstd посылать команды ардуине и забирать ответ из файла tmp.txt. Ниже представлен пример.


Загрузите скетч:


byte d13 = 0;
byte d12 = 0;

#define MAXMILLIS 4294967295
unsigned long timeobnv;
unsigned long timelapsed = 0;
int t = 22;


void setup() 
 {
   Serial.begin(57600);
   pinMode(13, OUTPUT);
   pinMode(12, OUTPUT);
 }
  
void loop() 
 {  
   char from_server; 
  
   while(Serial.available())
    {
       delay(1);
       from_server = Serial.read(); 

       // D13 
       if(from_server == 'A') 
         {
           digitalWrite(13, HIGH);
           d13 = 1;
         }

       if(from_server == 'a') 
         {
           digitalWrite(13, LOW);
           d13 = 0;
         }

       // D12
       if(from_server == 'B') 
         {
           digitalWrite(12, HIGH);
           d12 = 1;
         }

       if(from_server == 'b') 
         {
           digitalWrite(12, LOW);
           d12 = 0;
         }  
     
     } // конец while
     

   unsigned long currtime = millis(); 
 
   if(currtime > timeobnv) timelapsed = (currtime - timeobnv); 
 
   else timelapsed = (MAXMILLIS - timeobnv + currtime);

   if(timelapsed >= 250) 
    { 
      timeobnv = currtime;
      sendate();
    }
     
 } // конец loop


void sendate() // отправка данных
 {
   Serial.print(d13); 
   Serial.print(" "); 
   Serial.print(d12); 
   Serial.print(" "); 
   Serial.print(t); 
   Serial.print('\n'); 
 }


Ардуина будет отправлять ответ в виде строки 0 0 22, а в браузере он будет раскладываться по переменным.


Строка, посылаемая ардуиной, должна обязательно заканчиваться \n


Допустим вы всё делаете на роутере с установленным сервером uhttpd (установить можно так opkg install uhttpd). Тогда скачайте архив и распакуйте его в папку /www, получится так /www/ajax.


Запустите ardunetstd указав путь для файла tmp.txt так, чтоб он создавался в папке с сайтом:

/opt/ardunetstd /dev/ttyUSB0 57600 /www/ajax/tmp.txt 3495 3496 "192.168.1.224" 1 1 8080 /opt/index.web 1 350000



В файле index.html (из архива) впишите адрес роутера:


...
<script>
var dlina = 3; 
var stroka;
var adres = 'http://192.168.5.197:8080/='; # поменять нужно только ip-адрес
...



Открыв в браузере — адрес/ajax/, увидите следующую картинку…



Нажмите кнопку D13 и надпись станет ярче, на ардуине загорится светодиод, в терминале появятся знакомые сообщения, а на смартфоне нолик сменится на единичку.






Пошлите со смартфона символ a и кнопка D13 потускнеет.


То же самое на пхп — fopen(“/tmp/tmp.txt”,”r”) и т.д...




GPS


Как писалось выше, подключать можно не только ардуину. Если подключить gps-приёмник BU-353 и запустить ardunetstd указав скорость 4800…

/opt/ardunetstd /dev/ttyUSB0 4800 /tmp/tmp.txt 3495 3496 "192.168.1.224" 1 1 8080 /opt/index.web 1 350000


… то в терминал посыплются данные.





При желании можно подключить несколько девайсов, запустив несколько программ, только нужно указывать другие порты.




В итоге получился универсальный комбайн для взаимодействия с usb-девайсами.


На этом пожалуй всё, если что-то не понятно, спрашивайте в комментах.


Исходники
Android

ardunetstd

#include <string.h>
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <termios.h>  
#include <stdio.h>  
#include <signal.h>  
#include <unistd.h>  
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/wait.h> 

#define BUFSIZE 2048

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n";

char index_file[BUFSIZE]={0,};
char index_filepatch[128]={0,};

char bRead[BUFSIZE] = {0,}; 

char device[32]={0,};
char file_ardu_patch[128]={0,};
char str_iz_file[BUFSIZE] = {0,};
char adr_udp[24] = {0,};

unsigned long int speedport = 0;
unsigned int PORTR = 0; 
unsigned int PORTS = 0; 

int udp_s = 0; 
int udp_c = 0; 

unsigned int PORTW = 0;  
int web = 0; 
unsigned long int PAUSARD = 0;

int fd;

struct sockaddr_in server;
struct hostent *host; 

int sockfd;
pid_t udp_server;
pid_t web_server;
pid_t web_server2;


void error_log(char *err) 
 {  
   time_t t;
   time(&t);
   FILE *f;
   f = fopen("Error.log", "a"); 
   fprintf(f, "%s. ", err);
   fprintf(f, "%s", ctime( &t));  
   printf("Write to Error.log\n");
   fclose(f);
   kill(udp_server, SIGTERM);
   kill(web_server, SIGTERM);
   kill(web_server2, SIGTERM);
   exit(0);
 }


void read_index_file() 
 { 
   char result_sting[BUFSIZE]; 
   FILE *file; 
   file = fopen(index_filepatch,"r");

   if(file == NULL)
    {
      error_log("Error-Open index_file");
    }

   while(fgets(result_sting,sizeof(result_sting),file))
    {
      strcat(index_file, result_sting);
    }

   fclose(file);
 }


void open_port()  
 {  
     
   fd = open(device, O_RDWR | O_NOCTTY); 
 
   if(fd == -1) 
     {
       error_log("Error - NOT open port");
     }

   else  
     {  
       struct termios options;  
       tcgetattr(fd, &options);   

       switch(speedport)
       {
	case 4800:       
          cfsetispeed(&options, B4800); 
          cfsetospeed(&options, B4800); 
        break;

	case 9600:       
          cfsetispeed(&options, B9600); 
          cfsetospeed(&options, B9600); 
        break;

	case 19200:       
          cfsetispeed(&options, B19200); 
          cfsetospeed(&options, B19200); 
        break;

	case 38400:       
          cfsetispeed(&options, B38400); 
          cfsetospeed(&options, B38400); 
        break;

	case 57600:       
          cfsetispeed(&options, B57600); 
          cfsetospeed(&options, B57600); 
        break;

	case 115200:       
          cfsetispeed(&options, B115200); 
          cfsetospeed(&options, B115200); 
        break;

	default: 
        printf("Error - Speed_port\n");
        error_log("Error - Speed_port");
        break;
       }

       options.c_cflag |= (CLOCAL | CREAD); 
       options.c_iflag = IGNCR;
       options.c_cflag &= ~PARENB;  
       options.c_cflag &= ~CSTOPB;  
       options.c_cflag &= ~CSIZE;  
       options.c_cflag |= CS8;  
       options.c_cc[VMIN] = 1;  
       options.c_cc[VTIME] = 1;  
       options.c_lflag = ICANON;  
       options.c_oflag = 0;  
       options.c_oflag &= ~OPOST; 
       tcflush(fd, TCIFLUSH);
       tcsetattr(fd, TCSANOW, &options);  
     }  
 }


int main(int argc, char *argv[])  
 {  
   if(argc != 13) 
    {
      printf("Not argumets!\n");
      error_log("Not argumets");
    }
  
   strncpy(device, argv[1], 31); // arduina
   speedport = strtoul(argv[2], NULL, 0); // baud rate
   strncpy(file_ardu_patch, argv[3], 127); // путь к файлу ardu
   PORTR = strtoul(argv[4], NULL, 0); // порт для upd-сервера 3495 (приём)
   PORTS = strtoul(argv[5], NULL, 0); // порт для upd-клиента 3496 (отправка)
   strncpy(adr_udp, argv[6], 23); // адрес udp-сервера для отправки
   adr_udp[sizeof(adr_udp) - 1] = 0;
   udp_s = atoi(argv[7]); // запуск udp-сервера ( 0 - не запускать, 1 - запускать)
   udp_c = atoi(argv[8]); // запуск udp-клиента ( 0 - не запускать, 1 - запускать)
   PORTW = strtoul(argv[9], NULL, 0); // порт для web-сервера 8080
   strncpy(index_filepatch, argv[10], 127); // путь к файлу index.html
   web = atoi(argv[11]); // запуск web-сервера ( 0 - не запускать, 1 - запускать)
   PAUSARD = strtoul(argv[12], NULL, 0); // пауза перед приёмом


   FILE *f;
   f = fopen(file_ardu_patch, "w"); 
   if(f == NULL) 
    {
      error_log("Error-Create file");
    }

   fclose(f);

   open_port(); 
   sleep(2);
   tcflush(fd, TCIFLUSH);


////////////////////////////////// udp_server //////////////////////////////////////////
   if(udp_s) 
    {
      udp_server = fork();

      if(-1 == udp_server) 
       {
         error_log("Not udp_PID");
       }

     if(udp_server == 0) 
       {
        char msg[BUFSIZE] = {0,};
        char to_Ardu[BUFSIZE] = {0,};
        
	memset(&server, 0x00, sizeof(server));  
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = htonl(INADDR_ANY);
        server.sin_port = htons(PORTR);

        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
         {
           error_log("Not sockfd udp_server");
         }
 
        if (bind(sockfd, (struct sockaddr *) &server, sizeof(server)))
         {
           error_log("Not bind udp_server");
         }
 
        while(1)
         {
           memset(msg, 0, sizeof(msg));
           memset(to_Ardu, 0, sizeof(to_Ardu));
           int n = recvfrom(sockfd, msg, BUFSIZE - 1, 0, NULL, NULL); 
           msg[n] = 0;
           printf("UDP_reciv\n");
           snprintf(to_Ardu, BUFSIZE - 1, "echo '%s' > %s", msg, device);
           system(to_Ardu);
           printf("UDP_send_to_ardu: %s\n", msg);
         }
 
        close(sockfd);
        return EXIT_SUCCESS;

      } // END FORK udp
    }


////////////////////////////////// web_server //////////////////////////////////////////
if(web) 
 {
  web_server = fork();

  if(-1 == web_server) 
    {
      error_log("Not web_PID");
    }  

  if(web_server == 0) 
    {
      read_index_file();
      int n2 = 1, client_fd;
      struct sockaddr_in svr_addr, cli_addr;
      socklen_t sin_len = sizeof(cli_addr);
 
      int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

      if(sock < 0) 
       {
         close(sock);
         error_log("Error - socket web");
       }
  
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &n2, sizeof(n2));

      svr_addr.sin_family = AF_INET;
      svr_addr.sin_addr.s_addr = INADDR_ANY;
      svr_addr.sin_port = htons(PORTW); 
 
      if(bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) 
       {
         close(sock);
         error_log("Error - web bind");
       }
 
      if(listen(sock, 5) == -1) 
       {
         close(sock);
         error_log("Error - listen");
	}

      char buffer[BUFSIZE] = {0,};
      char bufRec[BUFSIZE] = {0,};

     while(1) 
     {
      client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);

      if(client_fd == -1) 
       {
         continue;
       }
 
      web_server2 = fork();

      if(-1 == web_server2) 
       {
         error_log("Not web_2_PID");
       }  

      if(web_server2 == 0) 
       {
         unsigned int i = 0;
         unsigned int ir = 0;

         memset(bufRec, 0, sizeof(bufRec));
         memset(buffer, 0, sizeof(buffer));

         read(client_fd, buffer, BUFSIZE - 1);

         if((strstr(buffer, "GET")) != NULL)
          {
           if((strstr(buffer, "curl")) != NULL) //////////////////////////// Curl
            {
              printf("OK_Curl\n");

              for(; i <= strlen(buffer); i++)
               {
                 if(buffer[i] == '/')
                  {
                    i++;
                    for(; i <= strlen(buffer); i++)
                      {
                        bufRec[ir] = buffer[i];
                        ir++;

                        if(buffer[i] == '\r' || buffer[i] == '\n')
                          {
                            bufRec[ir] = 0; 
                            break;
                          }
                      }

                    break; 
                  }
               }
            
              memset(buffer, 0, sizeof(buffer));
              char *p;

              if((p = strstr(bufRec, "HTTP")) != NULL)
               {   
                 int index = p - bufRec;
                 int i = 0;
                 for(; i <= index - 2; i++)
                  {
                    buffer[i] = bufRec[i];
                  }

                 buffer[i] = 0;
                 memset(bufRec, 0, sizeof(bufRec));
                 snprintf(bufRec, BUFSIZE - 1, "echo '%s' > %s", buffer, device);
                 system(bufRec);
                 printf("Curl_send_toArdu: %s\n", bufRec);
               }

              memset(bufRec, 0, sizeof(bufRec));
              memset(buffer, 0, sizeof(buffer));

              usleep(PAUSARD);

              FILE *f; 
              f = fopen(file_ardu_patch, "r"); // файл ардуины
              if(f == NULL) 
               {
                 close(client_fd);
                 error_log("Error-NOT open file");
               } 

              fgets(buffer, BUFSIZE - 1, f);
              fclose(f); 

              snprintf(bufRec, BUFSIZE - 1, "%s\r\n", buffer);
              write(client_fd, bufRec, sizeof(bufRec));
              close(client_fd); 
              printf("Send to curl_client: %s\n", bufRec); 
              exit(0);
            }

          else //////////////////////////////////////////////////////////////// GET
            {
              printf("OK_GET\n");

              for(; i <= strlen(buffer); i++)
               {
                 if(buffer[i] == '=')
                  {
                    i++;
                    for(; i <= strlen(buffer); i++)
                      {
                        bufRec[ir] = buffer[i];
                        ir++;

                        if(buffer[i] == '\r' || buffer[i] == '\n')
                          {
                            bufRec[ir] = 0; 
                            break;
                          }
                      }

                    break; 
                  }
               }
            

              memset(buffer, 0, sizeof(buffer));
              char *p;

              if((p = strstr(bufRec, "HTTP")) != NULL)
               {   
                 int index = p - bufRec;
                 int i = 0;
                 for(; i <= index - 2; i++)
                  {
                    buffer[i] = bufRec[i];
                  }

                 buffer[i] = 0;
                 memset(bufRec, 0, sizeof(bufRec));
                 snprintf(bufRec, BUFSIZE - 1, "echo '%s' > %s", buffer, device);
                 system(bufRec);
                 printf("GET_send_toArdu: %s\n", bufRec);
               }
           }
         }
  
       else 
        {
          printf("NOT GET or Curl\n");
          close(client_fd);
          exit(0);
        }

       memset(bufRec, 0, sizeof(bufRec));
       memset(buffer, 0, sizeof(buffer));

       usleep(PAUSARD);

       FILE *f; 
       f = fopen(file_ardu_patch, "r"); // файл ардуины

       if(f == NULL) 
        {
          close(client_fd);
          error_log("Error-NOT open file");
        } 

       fgets(buffer, BUFSIZE - 1, f);
       fclose(f); 

       snprintf(bufRec, BUFSIZE - 1, "%s", buffer);
       write(client_fd, response, sizeof(response) - 1);
       write(client_fd, index_file, BUFSIZE); 
       write(client_fd, bufRec, BUFSIZE);
       close(client_fd);
       printf("Send to web_client: %s\n", bufRec);
       exit(0);

     } // END FORK web2

     close(client_fd);
     wait(NULL);

   } // END while

 } // END fork web

} // END web

/////////////////////////////////////////// SEND UDP //////////////////////////////////////////////
 if(udp_c)
  {
   if((sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 
     {
       close(sockfd);
       error_log("Not sockfd");
     } 

   int n1 = 1;
   setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&n1,sizeof(n1));

   host = gethostbyname(adr_udp);
   memset(&server, 0, sizeof(server));
   server.sin_family = AF_INET;
   server.sin_port = htons(PORTS);
   server.sin_addr = *((struct in_addr*) host->h_addr);
  }
/////////////////////////////////////////// READ ARDUINO //////////////////////////////////////////////

  unsigned int i;

  while(!VINTR) 
   {  
      int bytes = 0;

      memset(bRead, 0, sizeof(bRead));

      if((bytes = read(fd, bRead, BUFSIZE))==-1) // read()
        {
          printf("Error_Read_from_Arduino\n");
        }

      for(i = 0; i <= strlen(bRead); i++)
       {
         if(bRead[i] == '\n')
          {
            bRead[i] = 0; 
            break;
          }

       } 

     //////////////////// Сравнение строк //////////////////////
     if(strcmp(bRead, str_iz_file)==0)
       {
         printf("Str_equally\n"); 
       }

     else
       { 
         printf("Str_NOT_equally\n"); 
         FILE *f;
         f = fopen(file_ardu_patch, "w"); 
         if(f == NULL) 
          {
            error_log("Error-Write to file");
          }

         fprintf(f, "%s", bRead);
         fclose(f);
         memcpy(str_iz_file, bRead, sizeof(str_iz_file)); 
       }
    
     printf("Reciv_from_arduino: %s | Bytes - %d\n", bRead, bytes);

     if(udp_c)
      {

        if(sendto(sockfd, str_iz_file, strlen(str_iz_file), 0, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0)
         {
           error_log("ERROR_udp_send");
         }

        printf("UDP_send: %s\n\n", str_iz_file);
      }
     
    } // END (while) 
 
  return 0;

 }  // END main()


// gcc -Wall -Wextra ardunetstd.c -o ardunetstd

###########################################
##########Makefile Openwrt#################
###########################################

include $(TOPDIR)/rules.mk

PKG_NAME:=ardunetstd
PKG_VERSION:=1
PKG_RELEASE:=1

PKG_BUILD_DIR:= $(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk

define Package/ardunetstd
	SECTION:=utils
	CATEGORY:=Utilities
	TITLE:=ardunetstd - ArduNetstD utility
endef

define Package/ardunetstd/description
    ardunetstd - ArduNetstD utility
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Compile
	$(TARGET_CC) $(TARGET_CFLAGS) -c -o $(PKG_BUILD_DIR)/ardunetstd.o $(PKG_BUILD_DIR)/ardunetstd.c
	$(TARGET_CC) $(TARGET_LDFLAGS) -o $(PKG_BUILD_DIR)/ardunetstd $(PKG_BUILD_DIR)/ardunetstd.o
endef

define Package/ardunetstd/install
	$(INSTALL_DIR) $(1)/
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/ardunetstd $(1)/
endef

$(eval $(call BuildPackage,ardunetstd))

//  make package/ardunetstd/compile V=s



GitHub


  • +154
  • 18655
Поддержать автора




Telegram-чат istarik

Задать вопрос по статье
Telegram-канал istarik

Известит Вас о новых публикациях






Комментарии (71)

0
stD подскажите, что, кроме кода запуска надо править, чтобы папку ajax сменить на другую. Мне удобнее была предыдущая — ymdom.(все действия делаю на ББ)
0
Получается не в смене папки проблема, а в смене имени файла, в котором инфа от арду хранится. Папку поменял и файл поменял — в итоге веб сервер работате, а кнопки на странице не нажимаются и температура не показывается. Название папки оставил изменённое, а название файла вернул на оригинал и весь функционал заработал.
0
Я не понял, Вы справились или нет?
0
да. название файла вернул ваше и всё заработало.
0
Если меняете название файла в строке запуска, то его нужно менять и в файле index.html…

show();
setInterval(show,620);  
function show(){  
            $.ajax({ 
                type: "GET",
	        url: "tmp.txt", !!!!!!!!!ВОТ ЗДЕСЬ!!!!!!!!!!!!!
                timeout:560,         
                cache: false,       
                success: function(data){  
                           var vars = data.split(" "); 
                           if(vars.length == dlina) 
                             {     
                               $('#eror').html("Connect!");
0
вот засада, я его в этом файле минут 10 искал. Вот спасибо.
0
Пожалуйста.
0
И снова здравствуйте!
На днях обновил убунту, стояла 14.04.1 х64, до 16.04.1 х64 и при запуске
sudo /opt/ardunetstd /dev/ttyS0 57600 /var/www/ymdom/arduino.tmp 3495 3496 "192.168.1.255" 1 1 8080 /opt/index.web 1 350000
ругается:
sudo: unable to execute /opt/ardunetstd: No such file or directory

если без sudo, то так:
-bash: /opt/ardunetstd: Нет такого файла или каталога

и даже если в папку зайти и от туда запустить, то
killerpaf@server:/opt$ ./ardunetstd /dev/ttyS0 57600 /var/www/ymdom/arduino.tmp 3495 3496 "192.168.1.224" 0 0 8080 /opt/index.web 1 350000
-bash: ./ardunetstd: Нет такого файла или каталога

killerpaf@server:~$ cd /opt
killerpaf@server:/opt$ ls
ardunetstd  index.web

Куда можно капнуть? Или в убунте новой что-то изменилось и ardunetstd не желает с ней контачить?
0
Попробуйте так:

sudo chmod +x /opt/ardunetstd
0
Неоднократно. Вот ваш сервер из конструктора запускается, но пишет еррор бинд., сейчас буду его ковырять.
0
А, на 80 порту nginx висит, запустил homestd на 81 и заработало, даже управление ардуиной пошло из консоли, просто скетч еще из конструктора не заливал. Кстати, а если homestd переименовать в ardunetstd и использовать. Параметры все будут из скрипта работать?
0
Вобщем что-то с ardunetstd не так. Сейчас даже создал другую папку и скопировал туда всё необходимое, набирая имена и команды вручную (обычно мс юзал), но все равно пишет, что нет такого файла при запуске.
0
0
sudo chmod +x /opt/ardunetstd

sudo chmod 777 /opt/ardunetstd

Может так? А Вы путь то к файлу не путаете?
0
Может Вы не для того устройства файл скачали?
0
я файл забэкапил с рабочего перед обновлением. Но чем не шутит, попробую перекачать.
0
Перекачал заново и тоже самое. Может что-то упустил. Система:
Linux server 4.4.0-42-generic #62-Ubuntu SMP Fri Oct 7 23:11:45 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Из установленного только apache2+nginx+php+mysql по вашей статье. Может быть я что-то не доустановил для работы вашего сервера?
0
Это чепуха какая-то происходит.

Скачали, положите его и файл index.web в домашнюю папку, сделайте:

sudo chmod +x /home/you/ardunetstd
sudo chmod 777 /home/you/ardunetstd


Запускайте.

sudo /home/you/ardunetstd /dev/ttyS0 57600 /tmp/arduino.tmp 3495 3496 "192.168.1.224" 0 0 8080 /home/you/index.web 1 350000
0
Про файл index.web то Вы не забыли?
0
попробовал уже и в дом кат. никак не хочет. сейчас пробую на другом компе, тоже с 16 убунтой
0
Да, что-то странное, на ноуте запустил, сразу всё пошло. И даже без сервера если ввести
cat /dev/ttyUSB0

то сразу данные от ардуины летят. А на серваке, где ругается на ardunetstd, после такой команды тишина. Только в ардуину отправить можно.
0
Очень может быть, что дело в том, что Вы обновились с 14 на 16, а там есть некоторые проблемы. Поставьте 16.04 на чистую систему.
комментарий был удален
0
И вобще при чём тут /dev/ttyS0

Вы же пишите…
cat /dev/ttyUSB0
0
killerpaf@server:~$ sudo chmod +x /home/killerpaf/mydom/ardunetstd
killerpaf@server:~$ sudo chmod 777 /home/killerpaf/mydom/ardunetstd
killerpaf@server:~$ sudo chmod +x /home/killerpaf/mydom/index.web
killerpaf@server:~$ sudo chmod 777 /home/killerpaf/mydom/index.web
killerpaf@server:~$ /home/killerpaf/mydom/ardunetstd /dev/ttyS0 57600 /tmp/tmp.tmp 3495 3496 "192.168.1.224" 0 0 81 /home/killerpaf/mydom/index.web 1 350000
-bash: /home/killerpaf/mydom/ardunetstd: Нет такого файла или каталога
0
Почему /dev/ttyS0, у Вас куда ардуина то подключена?
0
Ардуина в com port на матери подрублена, он так у меня определяется. Если что вот команда, с помощью которой показываются ком порты работающие реально, а не весь список из папке /dev
dmesg | grep tty
.
Всё победил проблему. Вобщем я как и думал что не доустановил что-то после обновления.
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install libc6:i386 libncurses5:i386 libstdc++6:i386

и всё заработало. Спс за помощь.
0
Вот пример выводаЖ
killerpaf@server:~$ dmesg | grep tty
[    0.000000] console [tty0] enabled
[    1.537370] 00:04: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
0
Пожалуйста.
0
Вопрос автору.
Если при запущенном ardunetstd отключить ардуину, терминал начинает выдавать ошибку и не прекращает при повторном подключении. Лечится это только перезапуском процесса. Как можно сделать так, чтобы при отключении ардуины, ardunetsd просто завершал свою работу?
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.