WillemVH commited on
Commit
941e798
·
verified ·
1 Parent(s): e509249

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +194 -0
app.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ import requests
3
+ import threading
4
+ import time
5
+ import json
6
+ import os
7
+
8
+ app = Flask(__name__)
9
+
10
+ # Global variables to track servo state
11
+ servo_state = {
12
+ 'a_pressed': False,
13
+ 'current_angles': [0, 0, 0],
14
+ 'rotation_active': False,
15
+ 'esp_ip': None
16
+ }
17
+
18
+ # Thread lock for thread safety
19
+ state_lock = threading.Lock()
20
+
21
+ def send_servo_command(servo_num, angle):
22
+ """Send command to specific servo"""
23
+ try:
24
+ with state_lock:
25
+ esp_ip = servo_state['esp_ip']
26
+
27
+ if not esp_ip:
28
+ return f"Error: No ESP32 IP configured"
29
+
30
+ url = f"http://{esp_ip}/{servo_num}?angle={angle}"
31
+ response = requests.get(url, timeout=1)
32
+ return f"Servo {servo_num} → {angle}°"
33
+ except Exception as e:
34
+ return f"Error: {e}"
35
+
36
+ def send_stop_command():
37
+ """Send emergency stop command"""
38
+ try:
39
+ with state_lock:
40
+ esp_ip = servo_state['esp_ip']
41
+
42
+ if not esp_ip:
43
+ return "Error: No ESP32 IP configured"
44
+
45
+ url = f"http://{esp_ip}/stop"
46
+ response = requests.get(url, timeout=1)
47
+ return "EMERGENCY STOP"
48
+ except Exception as e:
49
+ return f"Stop Error: {e}"
50
+
51
+ def continuous_rotation():
52
+ """Background thread for continuous rotation"""
53
+ ROTATION_SPEED = 5
54
+ ROTATION_DELAY = 0.1 # seconds between updates
55
+
56
+ while True:
57
+ with state_lock:
58
+ rotation_active = servo_state['rotation_active']
59
+ esp_ip = servo_state['esp_ip']
60
+
61
+ if rotation_active and esp_ip:
62
+ try:
63
+ # Rotate all servos
64
+ for i, servo_num in enumerate([1, 2, 3]):
65
+ with state_lock:
66
+ servo_state['current_angles'][i] += ROTATION_SPEED
67
+ if servo_state['current_angles'][i] > 180:
68
+ servo_state['current_angles'][i] = 0
69
+
70
+ angle = servo_state['current_angles'][i]
71
+
72
+ # Send command to servo
73
+ send_servo_command(servo_num, angle)
74
+
75
+ time.sleep(ROTATION_DELAY)
76
+ except Exception as e:
77
+ print(f"Rotation error: {e}")
78
+ time.sleep(0.5)
79
+ else:
80
+ time.sleep(0.1)
81
+
82
+ @app.route('/')
83
+ def index():
84
+ """Serve the main control page"""
85
+ return render_template('index.html')
86
+
87
+ @app.route('/set_ip', methods=['POST'])
88
+ def set_ip():
89
+ """Set the ESP32 IP address"""
90
+ data = request.get_json()
91
+ esp_ip = data.get('ip', '').strip()
92
+
93
+ if esp_ip:
94
+ with state_lock:
95
+ servo_state['esp_ip'] = esp_ip
96
+ return jsonify({'status': 'success', 'message': f'ESP32 IP set to: {esp_ip}'})
97
+ else:
98
+ return jsonify({'status': 'error', 'message': 'No IP provided'})
99
+
100
+ @app.route('/start_rotation', methods=['POST'])
101
+ def start_rotation():
102
+ """Start continuous rotation (equivalent to holding 'A')"""
103
+ with state_lock:
104
+ servo_state['rotation_active'] = True
105
+ servo_state['a_pressed'] = True
106
+
107
+ return jsonify({
108
+ 'status': 'success',
109
+ 'message': 'Continuous rotation started',
110
+ 'angles': servo_state['current_angles']
111
+ })
112
+
113
+ @app.route('/stop_rotation', methods=['POST'])
114
+ def stop_rotation():
115
+ """Stop rotation and send emergency stop (equivalent to releasing 'A')"""
116
+ with state_lock:
117
+ servo_state['rotation_active'] = False
118
+ servo_state['a_pressed'] = False
119
+
120
+ stop_msg = send_stop_command()
121
+
122
+ return jsonify({
123
+ 'status': 'success',
124
+ 'message': 'Rotation stopped',
125
+ 'stop_message': stop_msg,
126
+ 'angles': servo_state['current_angles']
127
+ })
128
+
129
+ @app.route('/reset_servos', methods=['POST'])
130
+ def reset_servos():
131
+ """Reset all servos to 0 degrees (equivalent to pressing 'R')"""
132
+ messages = []
133
+ with state_lock:
134
+ for i in range(3):
135
+ servo_state['current_angles'][i] = 0
136
+ msg = send_servo_command(i+1, 0)
137
+ messages.append(msg)
138
+
139
+ return jsonify({
140
+ 'status': 'success',
141
+ 'message': 'Servos reset to 0°',
142
+ 'details': messages,
143
+ 'angles': servo_state['current_angles']
144
+ })
145
+
146
+ @app.route('/set_angle', methods=['POST'])
147
+ def set_angle():
148
+ """Set specific angle for a servo"""
149
+ data = request.get_json()
150
+ servo_num = data.get('servo')
151
+ angle = data.get('angle')
152
+
153
+ try:
154
+ servo_num = int(servo_num)
155
+ angle = int(angle)
156
+
157
+ if servo_num < 1 or servo_num > 3:
158
+ return jsonify({'status': 'error', 'message': 'Servo number must be 1, 2, or 3'})
159
+
160
+ if angle < 0 or angle > 180:
161
+ return jsonify({'status': 'error', 'message': 'Angle must be between 0 and 180'})
162
+
163
+ with state_lock:
164
+ servo_state['current_angles'][servo_num-1] = angle
165
+
166
+ msg = send_servo_command(servo_num, angle)
167
+
168
+ return jsonify({
169
+ 'status': 'success',
170
+ 'message': f'Servo {servo_num} set to {angle}°',
171
+ 'details': msg,
172
+ 'angles': servo_state['current_angles']
173
+ })
174
+ except ValueError:
175
+ return jsonify({'status': 'error', 'message': 'Invalid servo number or angle'})
176
+
177
+ @app.route('/get_status', methods=['GET'])
178
+ def get_status():
179
+ """Get current servo status"""
180
+ with state_lock:
181
+ return jsonify({
182
+ 'a_pressed': servo_state['a_pressed'],
183
+ 'rotation_active': servo_state['rotation_active'],
184
+ 'current_angles': servo_state['current_angles'],
185
+ 'esp_ip': servo_state['esp_ip']
186
+ })
187
+
188
+ if __name__ == '__main__':
189
+ # Start the continuous rotation thread
190
+ rotation_thread = threading.Thread(target=continuous_rotation, daemon=True)
191
+ rotation_thread.start()
192
+
193
+ # Run Flask app
194
+ app.run(host='0.0.0.0', port=5000, debug=False)