WillemVH commited on
Commit
d957151
Β·
verified Β·
1 Parent(s): a60b2db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -126
app.py CHANGED
@@ -2,64 +2,78 @@ 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
  try:
23
  with state_lock:
24
  esp_ip = servo_state['esp_ip']
25
 
26
- # Use your ngrok URL directly
 
 
 
 
 
27
  url = f"http://{esp_ip}/{servo_num}?angle={angle}"
28
- print(f"Calling: {url}")
29
 
30
- response = requests.get(url, timeout=5)
31
- return f"Servo {servo_num} β†’ {angle}Β°"
32
 
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:
@@ -70,125 +84,72 @@ def continuous_rotation():
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)
 
2
  import requests
3
  import threading
4
  import time
5
+ import logging
6
+
7
+ # Set up logging
8
+ logging.basicConfig(level=logging.INFO)
9
+ logger = logging.getLogger(__name__)
10
 
11
  app = Flask(__name__)
12
 
 
13
  servo_state = {
 
14
  'current_angles': [0, 0, 0],
15
  'rotation_active': False,
16
+ 'esp_ip': "manipulatable-ungenitive-lorenzo.ngrok-free.dev" # Your ngrok URL
17
  }
18
 
 
19
  state_lock = threading.Lock()
20
 
21
  def send_servo_command(servo_num, angle):
22
+ """Send command to specific servo with detailed logging"""
23
  try:
24
  with state_lock:
25
  esp_ip = servo_state['esp_ip']
26
 
27
+ if not esp_ip:
28
+ error_msg = "No ESP32 IP configured"
29
+ logger.error(error_msg)
30
+ return error_msg
31
+
32
+ # Construct the URL - IMPORTANT: ngrok uses http (not https for free tier)
33
  url = f"http://{esp_ip}/{servo_num}?angle={angle}"
34
+ logger.info(f"πŸ”§ Sending to ESP32: {url}")
35
 
36
+ # Add a timeout and better error handling
37
+ response = requests.get(url, timeout=10)
38
 
39
+ logger.info(f"πŸ“‘ ESP32 Response: HTTP {response.status_code} - {response.text}")
 
 
 
 
 
 
 
40
 
41
+ if response.status_code == 200:
42
+ success_msg = f"Servo {servo_num} β†’ {angle}Β°"
43
+ logger.info(f"βœ… {success_msg}")
44
+ return success_msg
45
+ else:
46
+ error_msg = f"ESP32 returned HTTP {response.status_code}"
47
+ logger.error(f"❌ {error_msg}")
48
+ return error_msg
49
 
50
+ except requests.exceptions.ConnectTimeout:
51
+ error_msg = "Connection timeout - ngrok/ESP32 not responding"
52
+ logger.error(f"❌ {error_msg}")
53
+ return error_msg
54
+ except requests.exceptions.ConnectionError as e:
55
+ error_msg = f"Cannot connect to ESP32 via ngrok: {str(e)}"
56
+ logger.error(f"❌ {error_msg}")
57
+ return error_msg
58
  except Exception as e:
59
+ error_msg = f"Unexpected error: {str(e)}"
60
+ logger.error(f"❌ {error_msg}")
61
+ return error_msg
62
 
63
  def continuous_rotation():
64
  """Background thread for continuous rotation"""
65
  ROTATION_SPEED = 5
66
+ ROTATION_DELAY = 0.2 # seconds between updates
67
 
68
  while True:
69
  with state_lock:
70
  rotation_active = servo_state['rotation_active']
 
71
 
72
+ if rotation_active:
73
  try:
74
+ logger.info("πŸ”„ Rotation active - moving servos")
75
+ messages = []
76
+
77
  # Rotate all servos
78
  for i, servo_num in enumerate([1, 2, 3]):
79
  with state_lock:
 
84
  angle = servo_state['current_angles'][i]
85
 
86
  # Send command to servo
87
+ msg = send_servo_command(servo_num, angle)
88
+ messages.append(msg)
89
 
90
+ logger.info(f"🎯 Rotation completed: {messages}")
91
  time.sleep(ROTATION_DELAY)
92
+
93
  except Exception as e:
94
+ logger.error(f"πŸ’₯ Rotation error: {e}")
95
+ time.sleep(1)
96
  else:
97
  time.sleep(0.1)
98
 
99
+ # ... keep your existing routes but add a debug route ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ @app.route('/debug_esp')
102
+ def debug_esp():
103
+ """Test connection to ESP32 directly"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  try:
105
+ with state_lock:
106
+ esp_ip = servo_state['esp_ip']
 
 
 
107
 
108
+ if not esp_ip:
109
+ return jsonify({'status': 'error', 'message': 'No ESP32 IP configured'})
110
 
111
+ test_url = f"http://{esp_ip}/"
112
+ logger.info(f"πŸ§ͺ Testing ESP32 connection: {test_url}")
113
 
114
+ response = requests.get(test_url, timeout=10)
115
 
116
  return jsonify({
117
  'status': 'success',
118
+ 'message': f'ESP32 is reachable',
119
+ 'url_called': test_url,
120
+ 'http_status': response.status_code,
121
+ 'response_preview': response.text[:200] if response.text else 'Empty response'
122
  })
123
+
124
+ except Exception as e:
 
 
 
 
 
125
  return jsonify({
126
+ 'status': 'error',
127
+ 'message': f'Cannot reach ESP32: {str(e)}'
 
 
128
  })
129
 
130
+ @app.route('/test_servo')
131
+ def test_servo():
132
+ """Test sending a specific servo command"""
133
+ servo_num = request.args.get('servo', '1')
134
+ angle = request.args.get('angle', '90')
135
+
136
+ result = send_servo_command(int(servo_num), int(angle))
137
+
138
+ return jsonify({
139
+ 'servo': servo_num,
140
+ 'angle': angle,
141
+ 'result': result
142
+ })
143
+
144
+ # ... keep your existing start_rotation, stop_rotation, etc routes ...
145
+
146
  if __name__ == '__main__':
147
+ # Log startup info
148
+ logger.info("πŸš€ Starting Servo Controller")
149
+ logger.info(f"🎯 ESP32 Target: {servo_state['esp_ip']}")
150
+
151
  # Start the continuous rotation thread
152
  rotation_thread = threading.Thread(target=continuous_rotation, daemon=True)
153
  rotation_thread.start()
154
 
155
+ app.run(host='0.0.0.0', port=7860, debug=False)