WebSockets for Real-Time Communication

Programming 2 - Web Technologies

Building Interactive Web Applications

What is WebSocket?

WebSocket provides real-time, two-way communication between a client (browser) and server.

Traditional HTTP:

  • Client asks → Server responds → Connection closes
  • Like sending letters - wait for each response

WebSocket:

  • Client connects → Connection stays open → Both can send messages anytime
  • Like a phone call - continuous conversation

Perfect for: Live dashboards, chat apps, games, sensor monitoring - anything that needs instant updates!

💡 In Your Project: Arduino sensor data flows continuously to your web dashboard without page refreshes!

Step 1: Python Server Setup

Install Required Libraries:

pip install flask flask-socketio

Basic Server Structure:

  • Import Flask and SocketIO
  • Create Flask app
  • Initialize SocketIO
  • Run the server
from flask import Flask, render_template
from flask_socketio import SocketIO

# Create Flask app
app = Flask(__name__)

# Initialize SocketIO
socketio = SocketIO(app, cors_allowed_origins="*")

# Route for serving HTML
@app.route('/')
def index():
    return render_template('index.html')

# Run server
if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=8000)

Step 2: Handling Client Events (Python)

The @socketio.on() decorator:

Use this to listen for events sent from the browser.

Built-in Events:

  • 'connect' - When a client connects
  • 'disconnect' - When a client disconnects

💡 Tip: You can create custom event names for different purposes!

from flask_socketio import emit

# Handle new connection
@socketio.on('connect')
def handle_connect():
    print('Client connected!')
    # Send welcome message to this client
    emit('welcome', {'message': 'Hello from server!'})

# Handle disconnection
@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')

# Handle custom event from client
@socketio.on('my_custom_event')
def handle_custom(data):
    print('Received:', data)
    emit('response', {'reply': 'Got your message!'})

Step 3: Sending Data from Python

Two Ways to Send:

1. emit() - Send to current client only

2. socketio.emit() - Send to ALL clients (broadcast)

💡 Your Project: Use broadcast to send sensor data to all connected dashboards!

from flask_socketio import emit

# Send to ONE client (the one who triggered this)
@socketio.on('request_data')
def send_to_one():
    emit('data', {'temperature': 25.5})

# Send to ALL clients (broadcast)
def broadcast_sensor_data():
    temperature = 25.5  # Read from sensor
    
    socketio.emit('sensor_update', {
        'temp': temperature,
        'timestamp': time.time()
    })

# Example: Broadcast every 2 seconds
def background_task():
    while True:
        broadcast_sensor_data()
        time.sleep(2)

Example 1: Simple Counter (Python)

What it does:

  • Maintains a counter on the server
  • Client can request increment
  • All clients see the updated count
from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*")

counter = 0

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('connect')
def handle_connect():
    # Send current count to new client
    emit('counter_update', {'count': counter})

@socketio.on('increment')
def handle_increment():
    global counter
    counter += 1
    # Broadcast to ALL clients
    socketio.emit('counter_update', {'count': counter})

if __name__ == '__main__':
    socketio.run(app, port=8000)

Step 4: HTML Page Setup

Include Socket.IO Library:

Add the Socket.IO client library from CDN:

💡 Important: Include this BEFORE your own JavaScript code!

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Example</title>
</head>
<body>
    <h1>Real-Time Counter</h1>
    <div id="counter">0</div>
    <button id="incrementBtn">Increment</button>
    <div id="status">Disconnected</div>

    <!-- Include Socket.IO library -->
    <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
    
    <!-- Your JavaScript code -->
    <script>
        // Connect to server
        const socket = io();
    </script>
</body>
</html>

Step 5: JavaScript - Connecting to Server

The io() function:

Creates a connection to your server. By default, it connects to the same server that served the HTML page.

Built-in Events:

  • 'connect' - Connected successfully
  • 'disconnect' - Connection lost
  • 'error' - Connection error
// Initialize connection
const socket = io();

// Listen for successful connection
socket.on('connect', () => {
    console.log('Connected to server!');
    console.log('My ID:', socket.id);
    
    // Update UI
    document.getElementById('status').textContent = 'Connected';
});

// Listen for disconnection
socket.on('disconnect', () => {
    console.log('Disconnected from server');
    document.getElementById('status').textContent = 'Disconnected';
});

// Listen for errors
socket.on('error', (error) => {
    console.error('Error:', error);
});

Step 6: JavaScript - Receiving Data

The socket.on() method:

Listen for events sent from the Python server.

Pattern:

socket.on('event_name', (data) => {
    // Handle the data
});

💡 Tip: The event name in JavaScript must match the event name in Python!

// Listen for counter updates from server
socket.on('counter_update', (data) => {
    console.log('Received:', data);
    // data = { count: 5 }
    
    // Update the display
    document.getElementById('counter').textContent = data.count;
});

// Listen for welcome message
socket.on('welcome', (data) => {
    console.log('Server says:', data.message);
    alert(data.message);
});

// Listen for sensor data (your project)
socket.on('sensor_data', (data) => {
    // data = { dht: 25.5, mf52: 25.3 }
    document.getElementById('temp').textContent = data.dht + '°C';
});

Step 7: JavaScript - Sending Data

The socket.emit() method:

Send data from browser to Python server.

Pattern:

socket.emit('event_name', data);

You can send:

  • Strings, numbers, objects, arrays
  • Usually send objects for structured data
// Send simple event (no data)
socket.emit('increment');

// Send with data
socket.emit('set_counter', { value: 10 });

// Example: Button click sends event
document.getElementById('incrementBtn').addEventListener('click', () => {
    console.log('Sending increment request');
    socket.emit('increment');
});

// Example: Send user input
document.getElementById('sendBtn').addEventListener('click', () => {
    const message = document.getElementById('input').value;
    socket.emit('chat_message', {
        text: message,
        timestamp: Date.now()
    });
});

Example 1: Counter (Complete HTML)

Full working example:

This connects to the Python server from Slide 6 and creates a synchronized counter across all clients.

<!DOCTYPE html>
<html>
<head>
    <title>Counter Example</title>
</head>
<body>
    <h1>Shared Counter</h1>
    <div id="counter">0</div>
    <button id="incrementBtn">Increment</button>
    <div id="status">Disconnected</div>

    <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
    <script>
        const socket = io();
        
        socket.on('connect', () => {
            document.getElementById('status').textContent = 'Connected';
        });
        
        socket.on('counter_update', (data) => {
            document.getElementById('counter').textContent = data.count;
        });
        
        document.getElementById('incrementBtn').addEventListener('click', () => {
            socket.emit('increment');
        });
    </script>
</body>
</html>

Example 2: Simple Chat (Python)

What it does:

  • Receives messages from any client
  • Broadcasts to all connected clients
from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*")

@app.route('/')
def index():
    return render_template('chat.html')

@socketio.on('connect')
def handle_connect():
    print('New user connected')
    emit('system_message', {'text': 'Welcome to chat!'})

@socketio.on('send_message')
def handle_message(data):
    print(f"Message received: {data['message']}")
    
    # Broadcast to ALL clients
    socketio.emit('new_message', {
        'username': data['username'],
        'message': data['message']
    })

if __name__ == '__main__':
    socketio.run(app, port=8000)

Example 2: Simple Chat (HTML)

Complete chat client:

  • Input for username and message
  • Sends messages to server
  • Displays all messages
<div id="messages"></div>
<input id="username" placeholder="Your name">
<input id="messageInput" placeholder="Type message">
<button id="sendBtn">Send</button>

<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<script>
const socket = io();

socket.on('new_message', (data) => {
    const msgDiv = document.createElement('div');
    msgDiv.textContent = `${data.username}: ${data.message}`;
    document.getElementById('messages').appendChild(msgDiv);
});

document.getElementById('sendBtn').addEventListener('click', () => {
    const username = document.getElementById('username').value;
    const message = document.getElementById('messageInput').value;
    
    socket.emit('send_message', {
        username: username,
        message: message
    });
    
    document.getElementById('messageInput').value = '';
});
</script>

Example 3: Your Project (Python)

Continuous sensor broadcasting:

Uses background thread to read sensors and broadcast data every 2 seconds.

from flask import Flask, render_template
from flask_socketio import SocketIO
import threading
import time

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*")

def read_sensors():
    """Background task: continuously read and broadcast"""
    while True:
        # Read from Arduino (simplified)
        dht_temp = 25.5
        mf52_temp = 25.3
        
        # Broadcast to all clients
        socketio.emit('sensor_data', {
            'dht': dht_temp,
            'mf52': mf52_temp,
            'timestamp': time.strftime('%H:%M:%S')
        })
        
        time.sleep(2)

@socketio.on('connect')
def handle_connect():
    print('Dashboard connected')

if __name__ == '__main__':
    # Start background thread
    thread = threading.Thread(target=read_sensors, daemon=True)
    thread.start()
    
    socketio.run(app, port=8000)

Summary & Best Practices

Python Server:

  • @socketio.on('event') - Handle events from clients
  • emit('event', data) - Send to current client
  • socketio.emit('event', data) - Broadcast to all clients

JavaScript Client:

  • socket.on('event', callback) - Receive from server
  • socket.emit('event', data) - Send to server

✅ Best Practices:

  • Always handle 'connect' and 'disconnect' events
  • Use descriptive event names
  • Show connection status to users
  • Test with multiple browser windows

💡 Next Steps: Apply this to your temperature monitoring project!