0

I'm trying to write a flutter app for an IoT application. A mobile device will connect to the hardware via WiFi and data will be sent through a tcp socket. The IoT hardware is in development but I need to get a start on the app.

I got some help with a python script that runs a socket and outputs 2 sine waves that have been slightly randomized. It works well and I have a python host script that connects to it and plots the data as it comes through. I am going to pretend this is sensor data so I want to connect to this with flutter so that I can start working on my UI etc.

I've tried 1000 things but mostly worked through this: https://flutter.dev/docs/cookbook/networking/web-sockets.

Another thing I tried was using nodejs with socket.io and did manage to communicate with flutter using the adhara_socket_io package but there is so much abstracted away there that I worry its not replicating what I'll get from our hardware.

Here is the python code:

import queue
import time

#import matplotlib.pyplot as plt
import math
from queue import Queue

import numpy as np
import random
import socket
import threading
import asyncio


# A Queue in which the wave threads will store their values
# We will use this queue to get the data we need to send to our clients
Q = queue.Queue()

# replace this with your own host ip
HOST = '192.168.0.13'
PORT = 65432
Afrequency = float(input('give the frequency of the data output wave A: '))
Aperiod = int(input('give the output period (multiples of pi) of wave A: '))
Bfrequency = float(input('give the frequency of data output wave B: '))
Bperiod = int(input('give the output period (multiples of pi) of wave B: '))

# this function will continuosly generate x and y values for a given sine wave
# it waits 1/freq seconds so our frequency matches the given input


def generateTuples(name, outputfrequency, outputperiod):

    sincurveshift = 10
    rmax = 1.25
    rmin = 0.75
    outputperiod = outputperiod * math.pi
    numberOfPoints = outputfrequency * outputperiod
    increment = (2 * math.pi) / numberOfPoints
    xcounter = 0

    while True:
        jitterValue = rmin + (random.random() * (rmax - rmin))
        x = xcounter * increment
        sinValue = 5 * math.sin(x) + sincurveshift * jitterValue
        partOfTheMessage = '%s(%09.2f,%09.2f)*' % (name, x, sinValue)
        xcounter += 1
        Q.put(partOfTheMessage)
        time.sleep(1/outputfrequency)


# this is a thread that will be created each time a client connects
# it sends the total string with values from wave A and B
class clientThread(threading.Thread):
    def __init__(self, clientAddress, clientSocket, q):
        threading.Thread.__init__(self)
        self.clientSocket = clientSocket

        print("New client connected with address: " + str(clientAddress))

    def run(self):
        while True:
            self.clientSocket.send(bytes(Q.get(), 'utf-8'))
            time.sleep(0.1)


def main():
    # first we start up 2 threads that will each generate a y value every 1/freq seconds
    # we let them run in the background so we can move on to listening for clients
    print('starting sine wave 1')
    print('starting sine wave 2')
    wave1 = threading.Thread(target=generateTuples, args=(
        'A', Afrequency, Aperiod), daemon=True)
    wave2 = threading.Thread(target=generateTuples, args=(
        'B', Bfrequency, Bperiod), daemon=True)
    wave1.start()
    wave2.start()
    time.sleep(1)

    print('starting transmission')

    # here we set up the host
    # we continously look for clients and give each of them their own thread
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        s.listen()
        while True:
            conn, addr = s.accept()
            client = clientThread(addr, conn, queue)
            client.start()


if __name__ == "__main__":

    main()

And here is my flutter code.

import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/io.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
// This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MyApp Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'MyApp Mobile'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final channel = IOWebSocketChannel.connect('ws://192.168.0.13:65432');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: StreamBuilder(
        stream: channel.stream,
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
            case ConnectionState.none:
              print('trying to connect');
              return LinearProgressIndicator();
            case ConnectionState.active:
              print("OMG ITS CONNECTED");
              print(snapshot.data);
              return Text(snapshot.data);
            case ConnectionState.done:
              return Text('Its done ${snapshot.data}');
          }

          return Text(snapshot.hasData ? '${snapshot.data}' : 'No Data');
        },
      )),
    );
  }
}

I'm using my phone to run the app in development - not sure if that counts for anything. Any help is greatly appreciated!

N

EDIT - I'm now seeing an error in the python console:

New client connected with address: ('192.168.0.16', 65020)
Exception in thread Thread-24:
Traceback (most recent call last):
  File "C:\Users\nickc\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "host.py", line 62, in run
    self.clientSocket.send(bytes(Q.get(), 'utf-8'))
ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine

So it looks like it connects and then aborts the connection?

Like already mentioned by shubham in one of the answers I can sucessfully connect and print the incoming data to the console with:

Socket socket = await Socket.connect(host, port);
socket.listen((event) {
  print(String.fromCharCodes(event));
});
  • And the error you are getting is? – user207421 May 09 '20 at 02:36
  • It seems to be aborting my python server - I wasn't seeing this when I posted: From my python console. New client connected with address: ('192.168.0.16', 60629) Exception in thread Thread-20: Traceback (most recent call last): File "C:\Users\nickc\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner self.run() File "host.py", line 62, in run self.clientSocket.send(bytes(Q.get(), 'utf-8')) ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine – Nick Christensen May 09 '20 at 03:03
  • Not sure why someone would vote this question down... it's perfectly valid and will stop me getting help. – Nick Christensen May 09 '20 at 03:05

1 Answers1

2

I don't have much knowledge on the web socket channel package but when I tried running your code, I got broken pipe error on the python code as soon as my client(phone) connected and after going through this post I think the client socket is getting closed somehow.

But I am able to connect to your python socket using dart's socket package Here is the code

Socket socket = await Socket.connect(host, port);
socket.listen((event) {
  log(String.fromCharCodes(event));
});

Here is the output that I got

[log] B(000051.07,000013.89)*
[log] B(000051.11,000013.26)*
[log] B(000051.14,000016.09)*
[log] B(000051.18,000013.77)*
[log] B(000051.21,000015.63)*
[log] B(000051.25,000013.78)*
[log] B(000051.29,000013.49)*
[log] B(000051.32,000016.75)*
[log] A(000103.50,000012.36)*
[log] A(000103.58,000009.93)*
[log] A(000103.67,000007.72)*
[log] A(000103.75,000009.02)*
[log] A(000103.83,000008.28)*
Shubham Gupta
  • 1,917
  • 1
  • 10
  • 16
  • Thanks - whats weird is that when I run that on my phone the python console doesn't show an error but going to the AVD it has an error. I tried your code too - I can get get output printing to the console now which is much better than where I was. Thanks for the help. I will look into why the the other one is closing the socket – Nick Christensen May 09 '20 at 01:53
  • This answer helped a lot, thank you @Shubham Gupta I been looking for a solution to the same problem for a while. – Bola Gadalla Feb 24 '22 at 05:24