WebSocket работает на ПК, но не на Android? - PullRequest
0 голосов
/ 08 мая 2019

Я пытаюсь установить связь с WiFi-чипом ESP8266 с устройства Android, используя WebSockets для удаленного управления некоторыми двигателями, подключенными к Arduino. Сначала я создал прототип с использованием ПК и Python и настроил сервер WebSocket на ESP8266 ...

ESP8266_server.ino

#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>

const char* ssid     = "SKYNET";
const char* password = "yourdogsauntsmaidenname";


WebSocketsServer webSocket = WebSocketsServer(81);

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {

    //Serial.printf("[%u] get Message: %s\r\n", num, payload);

    switch(type) {
        case WStype_DISCONNECTED:      
            break;

        case WStype_CONNECTED: 
            {
              IPAddress ip = webSocket.remoteIP(num);
             // Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\r\n", num, ip[0], ip[1], ip[2], ip[3], payload);    
            }
            break;

        case WStype_TEXT:
            {
              //Serial.printf("[%u] get Text: %s\r\n", num, payload);
              String _payload = String((char *) &payload[0]);             
              Serial.print(_payload);           
            }   
            break;     

        case WStype_BIN:
            {
              hexdump(payload, lenght);
            }
            // echo data back to browser
            webSocket.sendBIN(num, payload, lenght);
            break;

    }
}

void setup() {

  // Set up serial comms
  Serial.begin(115200);

  // Set up WiFi 
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    // Serial.print(".");
     delay(200);
  }  
//  Serial.println("");
//  Serial.println("WiFi connected");  
//  Serial.println("IP address: ");
//  Serial.println(WiFi.localIP());

  delay(500);  

  // Set up web socket
//  Serial.println("Start Websocket Server");
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop() {
  webSocket.loop();
}

Чип WiFi подключен через последовательное соединение к Arduino Mega, который, в свою очередь, анализирует данные для управления некоторыми H-мостами для 2 двигателей постоянного тока. Arduino также передает данные на последовательный монитор Arduino IDE.

motor_control.ino

//  Serial comms from Wemos EPS8622 to Arduino Mega

#include "SoftPWM.h"

// Outputs to PWM & H bridge direction
// PWMs: 
// DC_pwm, left_pwm, right_pwm = 3, 4, 5
int pwm_out[] = {3, 4, 5};
const int num_pwm = sizeof(pwm_out) / sizeof(pwm_out[0]);

// H bridge direction control: 
// right_2, right_1, left_2, left_1, dc_2, dc_1  = 6, 7, 8, 9, 10 ,11
int dir_pins[] = {6, 7, 8, 9, 10 , 11};
const int num_dir_pins = sizeof(dir_pins) / sizeof(dir_pins[0]);

// Input buffer from ESP8266
int in_buff[] = {0, 0};
const int num_in_buff = sizeof(in_buff) / sizeof(in_buff[0]);

void setup() {

  // Set up serial comms
  Serial.begin(115200);   // Debug to Serial Monitor
  Serial3.begin(115200);  // From ESP8266

  // Set up PWM pins
  SoftPWMBegin();
  for (int i = 0; i < num_pwm; i++)
  {
    SoftPWMSet(pwm_out[i], 0);
  }

  // Set up H bridge direction pins
  for (int i = 0; i < num_dir_pins; i++)
  {
    pinMode(dir_pins[i], OUTPUT);
    digitalWrite(dir_pins[i], LOW);
  }
}

void loop() {

  // Check input from EPS8266 & send it to the H bridges
  while (Serial3.available() < num_in_buff){}

  if (Serial3.available()) {
      // read the incoming bytes
      // in_buff[0] - direction flags
      // in_buff[1] - PWM
      Serial.println(".......");
      Serial.println("In from ESP8266");
      for (int i = 0; i < num_in_buff; i++){
        in_buff[i] = Serial3.read();
        Serial.println(in_buff[i]);
      }
      Serial.println(".......");
  }

  // Set direcions on H bridges
  Serial.println("Direction pins");
  for (int i = 0; i < num_dir_pins; i++)
  {
    digitalWrite(dir_pins[i], bitRead(in_buff[0], i));
    Serial.print("pin ");
    Serial.print(dir_pins[i]);
    Serial.print(" : ");
    Serial.print(digitalRead(dir_pins[i]));
    Serial.println();
  }

  // PWM to motors
  for (int i = 0; i < num_pwm; i++)
  {
    SoftPWMSet(pwm_out[i], in_buff[1]);
  }
}

На ПК у меня есть некоторый код Python, который настроен как клиент WebSocket, который принимает пользовательский ввод с терминала и передает его на чип ESP.

motor_control_WiFi.py

"""
Motor control over WiFi using Wemos EPS8266 & Mega board

Motor H-bridge direction flags
    Both forwards     - b'111001, d'57, ASCII 9
    Both backwards    - b'110110, d'54, ASCII 6
    Clockwise (right) - b'111010, d'58, ASCII :
    A/Clock (left)    - b'110101, d'53, ASCII 5

"""

from ws4py.client.threadedclient import WebSocketClient
import time, requests
import struct

esp8266host = "ws://192.168.1.84:81/" 

class DummyClient(WebSocketClient):
    def opened(self):
        print("Websocket open")
    def closed(self, code, reason=None):
        print("Connexion closed down", code, reason)
    def received_message(self, m):
        print(m)

if __name__ == '__main__':
    try:
        ws = DummyClient(esp8266host)
        ws.connect()
        print("Ready !")
        direction = ""

        while direction != "q":

            direction = input("Direction: ")

            if direction == "f":
                payload = "9"
            if direction == "b":
                payload = "6"
            if direction == "r":
                payload = ":"
            if direction == "l":
                payload = "5"

            ws.send(payload)
            time.sleep(.20)

            pwm_out = int(input("PWM: "))
            pwm_out = struct.pack('<B', pwm_out)
            ws.send(pwm_out)
            time.sleep(.20)


        print("Finished, close Websocket connexion now and exit script")
        ws.send("0:0")
        ws.close()
        exit()

    except KeyboardInterrupt:
        ws.send("0:0")
        ws.close()

Это все отлично работает. Поэтому следующим моим шагом было создание приложения для Android, которое бы выполняло то, что делает мой код на Python. Я использую библиотеку okhttp3 и добавил implementation 'com.squareup.okhttp3:okhttp:3.6.0' в файл сборки Gradle и добавил интернет-разрешения для манифеста.

MainActivity.java

package qdivision.org.websocketexample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

public class MainActivity extends AppCompatActivity {

    private Button start;
    private TextView output;
    private OkHttpClient client;

    private final class EchoWebSocketListener extends WebSocketListener {
        private static final int NORMAL_CLOSURE_STATUS = 1000;

        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            //webSocket.send("l");
            webSocket.send(ByteString.decodeHex("23"));
            webSocket.send(ByteString.decodeHex("32"));
            webSocket.close(NORMAL_CLOSURE_STATUS, "Goodbye !");
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            output("Receiving : " + text);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            output("Receiving bytes : " + bytes.hex());
        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
            webSocket.close(NORMAL_CLOSURE_STATUS, null);
            output("Closing : " + code + " / " + reason);
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            output("Error : " + t.getMessage());
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start = (Button) findViewById(R.id.start);
        output = (TextView) findViewById(R.id.output);
        client = new OkHttpClient();

        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                start();
            }
        });

    }

    private void start() {
        Request request = new Request.Builder().url("ws://192.168.1.84:81").build(); //"ws://echo.websocket.org"
        EchoWebSocketListener listener = new EchoWebSocketListener();
        WebSocket ws = client.newWebSocket(request, listener);

        client.dispatcher().executorService().shutdown();
    }

    private void output(final String txt) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                output.setText(output.getText().toString() + "\n\n" + txt);
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="qdivision.org.websocketexample.MainActivity">

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Start !"
        android:layout_marginTop="40dp"
        android:textSize="17sp"/>

    <TextView
        android:id="@+id/output"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/start"
        android:layout_centerHorizontal="true"
        android:textSize="16sp"
        android:layout_marginTop="30dp"/>

</RelativeLayout>

Когда это развернуто, я не получаю никаких ошибок соединения. Отправленные байты возвращаются на дисплей, и клиент отключается без ошибок. НО через Arduino Serial Monitor я не получаю информацию о том, что что-либо отправлено!

Чего здесь не хватает? Почему не возникает ошибка соединения, если байты не отправляются / не принимаются?

Редактировать # 1 Я запустил код Python на устройстве Android из командной строки Termux. Код работает, говорит, что сокет открыт и позволяет мне вводить направление и ШИМ, но все еще ничего от последовательного монитора.

Редактировать # 2 Я могу пропинговать ESP8266 с устройства Android, используя Termux с потерей пакета 0, но все равно данные не отправляются ни кодом Python, ни приложением.

Edit # 3 Код Python теперь работает из командной строки Termux на устройстве Android (не знаю, как я не вносил изменений, он просто работает сейчас ?? !! ??). Приложение на основе okhttp3 по-прежнему не отправляет данные.

...