Close Menu
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
Facebook X (Twitter) Instagram
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us
Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
How To Electronics
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
How To Electronics
Home » DIY Spiderbot Hexapod Robot using ESP32-CAM FOV
ESP32-CAM Projects IoT Projects

DIY Spiderbot Hexapod Robot using ESP32-CAM FOV

Atlin AndersonBy Atlin AndersonUpdated:February 2, 20258 Mins Read
Share Facebook Twitter LinkedIn Telegram Reddit WhatsApp
DIY Spiderbot Hexapod Robot using ESP32-CAM FOV
Share
Facebook Twitter LinkedIn Pinterest Email Reddit Telegram WhatsApp

Overview

In this project, we build a DIY Spiderbot Hexapod Robot using ESP32CAM and SSC-32U servo controller. The robot can traverse multiple terrains and stream video using its camera. Notable for its cost-effectiveness and lightweight design, the robot integrates 18 micro servos within a sturdy frame. The assembly process is simplified through the use of plastic hole-and-pin connectors, eliminating the need for screws except for those needed for the servos. This project was sent in by the creator Atlin Anderson.

A standout feature of this hexapod is its FPV (First-Person View) camera, offering adjustable resolution to enhance the user experience. Control is facilitated through a custom Android application, connecting to the robot via a WiFi access point. This feature underscores the project’s emphasis on accessibility and user-friendly operation. The hexapod’s semi-modular electronic mount further contributes to its adaptability, supporting potential upgrades or modifications.

This project distinguishes itself through minimal electronics design, prioritizing ease of assembly and maintenance. Additionally, the customizable nature of the Android control app allows for personalization, tailoring the control interface to suit individual preferences.


Bill of Materials

Following are the list of components required to build DIY Spiderbot Hexapod Robot based on ESP32-CAM Module.

S.N.ComponentsQuantityPurchase Link
1ESP32 Cam Module1
2FTDI Module1
3SSC-32U Servo Controller1
4Goteck Servo22
5Switch SPST1
6Bread Board Jumpers1
77.4V 2000mah Li Battery2
8Li Battery Charger2
9Deans male connector6
105V 9A setpdown voltage regulator1
112000uF Capaictor8
12Servo cable extenders1
132W LED COB4
14110 ohm resistor1
15BC547 NPN BJT transistor1
16JST connector (for LED)20
17PLA filament-
18TPU filament-




What is a Hexapod Robot?

A hexapod robot is a six-legged mechanical device that mimics the movement of insects, offering stability and flexibility in various terrains.

Hexapod Robot

It can perform complex maneuvers such as turning and climbing, thanks to the independent control of each leg. Hexapods are utilized in diverse fields, including research, education, and exploration, benefiting from their ability to navigate difficult environments more effectively than wheeled robots. Their design ranges from simple to highly complex systems, incorporating advanced features like obstacle avoidance, inverse kinematics, and autonomous navigation.


Hardware & Circuit

The circuit diagram depicts the electrical connections for a Hexapod (SpiderBot) Robot with an ESP32-CAM and a servo controller.

DIY Spiderbot Hexapod Robot Circuit Diagram

  • Power Supply:
    • A 7.4V LiPo battery is used to power the circuit, connected through a JST connector.
    • A USB 7.4 LiPo removable charger connects to the battery for recharging.
    • A voltage regulator (D24V90F5) steps down the voltage to 5V, suitable for powering the ESP32-CAM and servos.
    • An SPST (Single Pole Single Throw) switch is used to control the power to the circuit.
  • ESP32-CAM:
    • This is the microcontroller and camera module that can be used for streaming video and controlling the robot.
    • It is powered by the 5V output from the voltage regulator.
    • A brownout prevention capacitor is placed close to the ESP32-CAM to stabilize the power supply and prevent the ESP32 from resetting during voltage dips.
    • The Ground (GND) from the voltage regulator is connected to the ESP32-CAM’s GND.
  • SSC-32U Servo Controller:
    • This is a 32-channel servo controller used for controlling multiple servos of the hexapod.
    • It is connected to the ESP32-CAM via the RX (Receive) pin for control signals.
    • The VS1 input on the servo controller receives power from the voltage regulator.
    • VS is connected to VL
  • Servos:
    • Multiple servos are connected to the servo controller. Each servo connection is labeled (e.g., SERVO LEG 6 COXA) indicating which part of the hexapod it controls.
  • LED and Transistor:
    • A 2W 5V LED COB (Chip On Board) serves as a light source for the camera to see in dark conditions.
    • A 110R resistor limits the current to the LED to prevent it from burning out.
    • A B547 (NPN BJT) transistor acts as a switch, controlling the LED’s power state from an IO4 from the ESP32-CAM
  • Connectors and Misc:
    • A Deans connector is used for a secure power connection.
    • A micro JST connector is noted, which allows the lightbar of the circuit to be disconnected when needed.



Software Structure for Operating Hexapod Robot

The diagram illustrates a loop where the user sends commands through the Android app, the ESP32-CAM receives and processes these commands, and then relays instructions to the servo controller to perform the desired actions.

This setup allows for real-time control of the robot’s movements and functions via the Android app.

Android App Side:

  • A user interacts with the app by pressing an action button.
  • Upon this interaction, the app sends a UDP (User Datagram Protocol) packet to the target IP address, which is the address of the ESP32-CAM module.

ESP32-CAM Side:

  • The ESP32-CAM acts as a wifi access point and is continuously running code that listens for incoming UDP packets.
  • Once a packet is received, the ESP32-CAM checks for specific actions. These actions could be one-time commands like toggling a headlight or continuous commands like moving.
  • If the action requires a continuous operation (e.g., walking, stopping), the ESP32-CAM updates the operation if a different packet is received with a new command.
  • After processing the command, the ESP32-CAM sends a corresponding command string to the servo controller, which then translates this into the movements of the robot’s servos.



Source Code/Program

Let us take a look at the code for DIY Spiderbot Hexapod Robot made using ESP32-CAM Module. The code written by Atlin Anderson can be found on the GitHub Repository.

The code is for controlling a six-legged (hexapod) robot equipped with an ESP32-CAM, which functions as both the controller and video streamer. Here’s a breakdown of the main functionalities of the code:

  • Initialization and Configuration: The script initializes serial communication, sets up the ESP32-CAM with specific configurations, and establishes a WiFi access point named “ESP32 Spiderbot”. It also configures a web server to handle requests for streaming video in different resolutions and formats.
  • Servo Control: It defines variables and functions for controlling the servos that move the robot’s legs. Each leg has three servos (coxa, femur, tibia) and the code includes functions to move these servos to specific angles to achieve walking, rotating, strafing, and other movements.
  • UDP Communication: The robot listens for UDP packets on a specific port which controls the robot’s actions like walking forward, backward, rotating, etc., based on commands received over the network.
  • Web Server Functions: The server provides endpoints to access live video feeds from the ESP32-CAM in different formats and resolutions (BMP, low-resolution JPEG, high-resolution JPEG, and MJPEG stream).
  • Multi-threading: A task is created to handle web server clients separately, ensuring that video streaming operations do not block the main control loop of the robot.

This code effectively makes the spiderbot a remotely controlled robot with live video feedback, showcasing how it can be controlled and monitored via a network.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
//created by ATLIN ANDERSON
//BOARD: AI THINKER ESP32-CAM
//ESP32 CAM board used
 
#include <WebServer.h>
#include <WiFi.h>
#include <esp32cam.h>
#include <WiFiUdp.h>
 
/////////////////////////////////////////////////////////////
//SPIDERBOT Global Variables/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
 
//    //spider leg assignment diagram
//    //
//    //    Front
//    //
//    //leg 2     leg 1
//    //     \   /
//    // leg4- 0 -leg 3
//    //     /  \
//    // leg 6   leg 5
//
//    //group1: 1, 4, 5
//    //group2: 2, 3, 6
 
int max_servo_angle = 85;
 
//leg1 servo binding
int servo_coxa_1 = 8;
int servo_femur_1 = 9;
int servo_tibia_1 = 10;
//leg2 servo binding
int servo_coxa_2 = 24;
int servo_femur_2 = 25;
int servo_tibia_2 = 26;
//leg3 servo binding
int servo_coxa_3 = 4;
int servo_femur_3 = 5;
int servo_tibia_3 = 6;
//leg4 servo binding
int servo_coxa_4 = 20;
int servo_femur_4 = 21;
int servo_tibia_4 = 22;
//leg5 servo binding
int servo_coxa_5 = 0;
int servo_femur_5 = 1;
int servo_tibia_5 = 2;
//leg6 servo binding
int servo_coxa_6 = 16;
int servo_femur_6 = 17;
int servo_tibia_6 = 18;
 
//spider control variables
//default speed of 400ms
int servo_speed = 400;
int servo_speed_old = 400;
 
//spider control variables
//height from 0-9
int spider_height = 5;
int spider_height_old = 5;
 
//default standing angles
int default_standing_coxa_angle = 0;
int default_standing_femur_angle = 0;
int default_standing_tibia_angle = 90;
 
//used for timing of movement command
unsigned long milli_time_reference = 0;
 
//stores all commands to be sent to servo controller
String servo_serial_command_buffer = "";
String servo_serial_command_buffer_old = "";
 
//adds buffer time between spiderbot action groups
int added_time_to_movement_cycle_min = 200;
 
//used for tracking if calibration is enabled
bool calibration_tracker = false;
 
/////////////////////////////////////////////////////////////
//Network setup global variables/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
 
//Wifi Access Point credentials
//Set desired SSID name and password
const char* WIFI_SSID = "ESP32 Spiderbot";
const char* WIFI_PASS = "0123456789";
 
// //setting up recieval of UDP Packets
WiFiUDP Udp;
unsigned int localUdpPort = 4210;  // local port to listen on
char incomingPacket[2];            // buffer for incoming packets
char old_Packet[2];                // buffer for old packets
//the first char is the coded action & the section char is either speed or height, if applicable
 
//initate webserver
WebServer server(80);
 
//initate a task (to run the camera stream wbeserver on the 2nd core of the ESP32Cam)
TaskHandle_t Task1;
 
///////////////////////////////////////////////////////////////
////ESP32cam Variables/////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
 
static auto loRes = esp32cam::Resolution::find(320, 240);
static auto hiRes = esp32cam::Resolution::find(800, 600);
 
 
void setup() {
  //enable the GPIO port for LED headlight
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
 
  Serial.begin(9600);
 
  //configure ESP32CAM
  {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(loRes);
    cfg.setBufferCount(2);
    //sewt resultion from 0-100 , 100 being best
    cfg.setJpeg(50);
 
    bool ok = Camera.begin(cfg);
    //Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
  }
 
  //set up wifi access point
  WiFi.softAP(WIFI_SSID, WIFI_PASS);  // Start the access point
 
  //enable the ESP32Cam camera server
  server.on("/cam.bmp", handleBmp);
  server.on("/cam-lo.jpg", handleJpgLo);
  server.on("/cam-hi.jpg", handleJpgHi);
  server.on("/cam.jpg", handleJpg);
  server.on("/cam.mjpeg", handleMjpeg);
  server.begin();
 
  //set up UDP packets
  Udp.begin(localUdpPort);
 
  //create a task that to operate ESP32cam on core 1
  xTaskCreatePinnedToCore(
    Task1code, /* Task function. */
    "Task1",   /* name of task. */
    10000,     /* Stack size of task */
    NULL,      /* parameter of the task */
    1,         /* priority of the task */
    &Task1,    /* Task handle to keep track of created task */
    0);        /* pin task to core 0 */
 
  ///SPIDERBOT SETUP////////////////////////////////////////////////////////////////////////
 
  //set time reference
  milli_time_reference = millis();
 
  //lay flat on ground
  leg_group_1_command(0, 0, 0);
  leg_group_2_command(0, 0, 0);
  send_serial_command();
 
  delay(2000);
 
  //default standing position
  leg_group_1_command(default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
  leg_group_2_command(default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
  send_serial_command();
 
  delay(1000);
}
 
void loop() {
  //check for and read packet
  if (read_UDP_packet() == true) {
    //functions only preformed when packet is recieved
    toggle_LED_headlight();
    toogle_calibration_mode();
    update_body_height();
    update_servo_speed();
  }
 
  //read & decode the packet & preform action
 
  //stop moving and move to default position
  if (incomingPacket[0] == 'S') {
    brake();
  }
 
  //walk forward
  if (incomingPacket[0] == 'W') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    walk_forward(milli_time_reference, servo_speed);
  }
 
  //walk backward
  if (incomingPacket[0] == 'X') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    walk_backward(milli_time_reference, servo_speed);
  }
 
  //strafe left
  if (incomingPacket[0] == 'A') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    strafe_left(milli_time_reference, servo_speed);
  }
 
  //strafe right
  if (incomingPacket[0] == 'D') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    strafe_right(milli_time_reference, servo_speed);
  }
 
  //rotate left
  if (incomingPacket[0] == 'Q') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    rotate_left(milli_time_reference, servo_speed);
  }
 
  //rotate right
  if (incomingPacket[0] == 'E') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    rotate_right(milli_time_reference, servo_speed);
  }
 
  //wave front arm
  if (incomingPacket[0] == 'H') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    greeting(milli_time_reference, servo_speed);
  }
 
  //dance
  if (incomingPacket[0] == 'V') {
    movement_state_transition(old_Packet[0], incomingPacket[0]);
    dance(milli_time_reference, servo_speed);
  }
 
  send_serial_command();
 
  old_Packet[0] = incomingPacket[0];
 
  delay(10);
}
 
bool read_UDP_packet() {
  //read UDP Packet
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    // receive incoming UDP packets
    int len = Udp.read(incomingPacket, 255);
    if (len > 0)
    {
      incomingPacket[len] = 0;
    }
    //Serial.printf("UDP packet contents: %s\n", incomingPacket);
    //Serial.print(incomingPacket);
    return true;
  }
  return false;
}
 
void handleBmp()
{
  if (!esp32cam::Camera.changeResolution(loRes)) {
    //Serial.println("SET-LO-RES FAIL");
  }
 
  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    //Serial.println("CAPTURE FAIL");
    server.send(503, "", "");
    return;
  }
  //Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),static_cast<int>(frame->size()));
 
  if (!frame->toBmp()) {
    //Serial.println("CONVERT FAIL");
    server.send(503, "", "");
    return;
  }
  //Serial.printf("CONVERT OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),static_cast<int>(frame->size()));
 
  server.setContentLength(frame->size());
  server.send(200, "image/bmp");
  WiFiClient client = server.client();
  frame->writeTo(client);
}
 
void serveJpg()
{
  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    //Serial.println("CAPTURE FAIL");
    server.send(503, "", "");
    return;
  }
  //Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),static_cast<int>(frame->size()));
 
  server.setContentLength(frame->size());
  server.send(200, "image/jpeg");
  WiFiClient client = server.client();
  frame->writeTo(client);
}
 
void handleJpgLo()
{
  if (!esp32cam::Camera.changeResolution(loRes)) {
    //Serial.println("SET-LO-RES FAIL");
  }
  serveJpg();
}
 
void handleJpgHi()
{
  if (!esp32cam::Camera.changeResolution(hiRes)) {
    //Serial.println("SET-HI-RES FAIL");
  }
  serveJpg();
}
 
void handleJpg()
{
  server.sendHeader("Location", "/cam-hi.jpg");
  server.send(302, "", "");
}
 
void handleMjpeg()
{
  if (!esp32cam::Camera.changeResolution(hiRes)) {
    //Serial.println("SET-HI-RES FAIL");
  }
 
  //Serial.println("STREAM BEGIN");
  WiFiClient client = server.client();
  auto startTime = millis();
  int res = esp32cam::Camera.streamMjpeg(client);
  if (res <= 0) {
    //Serial.printf("STREAM ERROR %d\n", res);
    return;
  }
  auto duration = millis() - startTime;
  //Serial.printf("STREAM END %dfrm %0.2ffps\n", res, 1000.0 * res / duration);
}
 
//Task1code:operates the ESP32 Camera server
void Task1code( void * pvParameters )
{
  for (;;) {
    server.handleClient();
  }
}
 
void toggle_LED_headlight() {
  //toggle LED Headlight
  if (incomingPacket[0] == 'F')
  {
    digitalWrite(4, !digitalRead(4));
    incomingPacket[0] = old_Packet[0];
  }
}
 
void toogle_calibration_mode() {
  //toggle calibration mode
  if (incomingPacket[0] == 'C')
  {
    if (calibration_tracker == false)
    {
      leg_group_1_command (0, 0, 0);
      leg_group_2_command (0, 0, 0);
      calibration_tracker = true;
    }
    else
    {
      leg_group_1_command (-45, 45, 45);
      leg_group_2_command (-45, 45, 45);
      calibration_tracker = false;
    }
  }
}
 
void update_servo_speed() {
  //update servo speed
  if (incomingPacket[0] == 'P')
  {
    if (incomingPacket[1] == '1')
    {
      update_servo_speed_int(1);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '2')
    {
      update_servo_speed_int(2);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '3')
    {
      update_servo_speed_int(3);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '4')
    {
      update_servo_speed_int(4);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '5')
    {
      update_servo_speed_int(5);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '6')
    {
      update_servo_speed_int(6);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '7')
    {
      update_servo_speed_int(7);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '8')
    {
      update_servo_speed_int(8);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '9')
    {
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
  }
}
 
void update_body_height() {
  //update Spider height
  if (incomingPacket[0] == 'H')
  {
    if (incomingPacket[1] == '1')
    {
      update_defeault_leg_angles(1);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '2')
    {
      update_defeault_leg_angles(2);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '3')
    {
      update_defeault_leg_angles(3);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '4')
    {
      update_defeault_leg_angles(4);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '5')
    {
      update_defeault_leg_angles(5);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '6')
    {
      update_defeault_leg_angles(6);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '7')
    {
      update_defeault_leg_angles(7);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '8')
    {
      update_defeault_leg_angles(8);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
    if (incomingPacket[1] == '9')
    {
      update_defeault_leg_angles(9);
      incomingPacket[0] = old_Packet[0];
      incomingPacket[1] = old_Packet[1];
    }
  }
}
 
void send_serial_command() {
  //send the final serial command string appended with servo speed
  if (servo_serial_command_buffer.length() > 0 && servo_serial_command_buffer != servo_serial_command_buffer_old || servo_speed_old != servo_speed || spider_height_old != spider_height)
  {
    Serial.println(servo_serial_command_buffer + "T" + String(servo_speed));
  }
  //then reset the string for new command
  servo_serial_command_buffer_old = servo_serial_command_buffer;
  servo_serial_command_buffer = "";
}
 
void update_servo_speed_int(int speed_int) {
  int max_servo_speed = 150;
  int min_servo_speed = 1000;  //max is 850 but tends to cause robot errors
  speed_int = constrain(speed_int, 1, 9);
  servo_speed = map(speed_int, 1, 9, min_servo_speed, max_servo_speed);
  servo_speed_old = servo_speed;
}
 
void movement_state_transition(char old_command, char new_command) {
  int transition_delay = servo_speed + 150;  //was 50
 
  if (old_command != new_command) {
    delay(transition_delay);
    brake();
    servo_serial_command_buffer = "";
  }
}
 
 
void update_defeault_leg_angles(int normalized_spider_height) {
  default_standing_femur_angle = map(normalized_spider_height, 1, 9, 45, -45);
  default_standing_tibia_angle = 90 + default_standing_femur_angle;
 
  spider_height = normalized_spider_height;
  spider_height_old = normalized_spider_height;
}
 
 
void servo_position(int servo_num, int servo_angle, int servo_speed) {
  //speed 100-9999
  servo_angle = map(servo_angle, -max_servo_angle, max_servo_angle, -90, 90);
  servo_angle = map(servo_angle, -90, 90, 500, 2500);
  servo_angle = constrain(servo_angle, 500, 2500);
  Serial.print("#" + String(servo_num) + "P" + String(servo_angle) + "T" + String(servo_speed));
  return;
}
 
void multi_servo_position(int servo_num1, int servo_angle1, int servo_num2, int servo_angle2, int servo_num3, int servo_angle3) {
  //speed 100-9999
  servo_angle1 = map(servo_angle1, -max_servo_angle, max_servo_angle, -90, 90);
  servo_angle1 = map(servo_angle1, -90, 90, 500, 2500);
  servo_angle1 = constrain(servo_angle1, 500, 2500);
 
  servo_angle2 = map(servo_angle2, -max_servo_angle, max_servo_angle, -90, 90);
  servo_angle2 = map(servo_angle2, -90, 90, 500, 2500);
  servo_angle2 = constrain(servo_angle2, 500, 2500);
 
  servo_angle3 = map(servo_angle3, -max_servo_angle, max_servo_angle, -90, 90);
  servo_angle3 = map(-servo_angle3, -90, 90, 500, 2500);
  servo_angle3 = constrain(servo_angle3, 500, 2500);
 
  //Serial.print("#" + String(servo_num1) + "P" + String(servo_angle1) + "#" + String(servo_num2) + "P" + String(servo_angle2) + "#" + String(servo_num3) + "P" + String(servo_angle3) + "T" + String(servo_speed) + "\r\n");
  servo_serial_command_buffer = servo_serial_command_buffer + "#" + String(servo_num1) + "P" + String(servo_angle1) + "#" + String(servo_num2) + "P" + String(servo_angle2) + "#" + String(servo_num3) + "P" + String(servo_angle3);
 
  return;
}
 
void servo_test() {
  for (int i = 0; i <= 23; i++) {
    servo_position(i, -90, 100);
    delay(1000);
    servo_position(i, 90, 100);
    delay(1000);
    servo_position(i, 0, 100);
    delay(1000);
  }
}
 
void specific_leg_relative_command(int leg_ID, int coxa_servo_angle, int femur_servo_angle, int tibia_servo_angle) {
  //spider leg assignment diagram
  //    Front
  //
  //leg 2     leg 1
  //     \   /
  // leg4- 0 -leg 3
  //     /   \
  // leg 6   leg 5
 
  //group1: 1, 4, 5
  //group2: 2, 3, 6
 
  switch (leg_ID) {
 
    //command group1
    case 1:
      multi_servo_position(servo_coxa_1, -coxa_servo_angle, servo_femur_1, -femur_servo_angle, servo_tibia_1, tibia_servo_angle);
      break;
    case 2:
      multi_servo_position(servo_coxa_2, coxa_servo_angle, servo_femur_2, femur_servo_angle, servo_tibia_2, -tibia_servo_angle);
      break;
    case 3:
      multi_servo_position(servo_coxa_3, -coxa_servo_angle, servo_femur_3, -femur_servo_angle, servo_tibia_3, tibia_servo_angle);
      break;
    //command group 2
    case 4:
      multi_servo_position(servo_coxa_4, coxa_servo_angle, servo_femur_4, femur_servo_angle, servo_tibia_4, -tibia_servo_angle);
      break;
    case 5:
      multi_servo_position(servo_coxa_5, -coxa_servo_angle, servo_femur_5, -femur_servo_angle, servo_tibia_5, tibia_servo_angle);
      break;
    case 6:
      multi_servo_position(servo_coxa_6, coxa_servo_angle, servo_femur_6, femur_servo_angle, servo_tibia_6, -tibia_servo_angle);
      break;
    default:
      break;
  }
}
 
void leg_group_1_command(int coxa_servo_angle, int femur_servo_angle, int tibia_servo_angle) {
  //legs 1, 4, 5
  //  //coxa
 
  multi_servo_position(servo_coxa_1, -coxa_servo_angle, servo_coxa_4, coxa_servo_angle, servo_coxa_5, coxa_servo_angle);
 
  //  //femur
 
  multi_servo_position(servo_femur_1, -femur_servo_angle, servo_femur_4, femur_servo_angle, servo_femur_5, femur_servo_angle);
  //
  //  //tibia
 
  multi_servo_position(servo_tibia_1, -tibia_servo_angle, servo_tibia_4, tibia_servo_angle, servo_tibia_5, tibia_servo_angle);
}
 
void leg_group_2_command(int coxa_servo_angle, int femur_servo_angle, int tibia_servo_angle) {
  //legs 2, 3, 6
  //  //coxa
 
  multi_servo_position(servo_coxa_2, coxa_servo_angle, servo_coxa_3, -coxa_servo_angle, servo_coxa_6, -coxa_servo_angle);
 
  //  //femur
 
  multi_servo_position(servo_femur_2, femur_servo_angle, servo_femur_3, -femur_servo_angle, servo_femur_6, -femur_servo_angle);
 
  //  //tibia
 
  multi_servo_position(servo_tibia_2, tibia_servo_angle, servo_tibia_3, -tibia_servo_angle, servo_tibia_6, -tibia_servo_angle);
}
 
void brake() {
  //stand still
  leg_group_1_command(default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
  leg_group_2_command(default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
}
 
void walk_forward(int reference_time_millis, int servo_speed) {
 
  //total seperate actions preformed
  int action_group_count = 4;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
    leg_group_2_command(42, 85, 85);                                                     //leg group 2 forward and up
    leg_group_1_command(0, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 1 back to default pos
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
    leg_group_2_command(42, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 2 down
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
    leg_group_2_command(0, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 2 back to default pos
    leg_group_1_command(42, 85, 85);                                                     // leg group 1 fowrd and up
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
    leg_group_1_command(42, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 1 down
  }
}
 
void walk_backward(int reference_time_millis, int servo_speed) {
 
  //total seperate actions preformed
  int action_group_count = 4;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
    leg_group_2_command(-42, 85, 85);                                                    //leg group 2 forward and up
    leg_group_1_command(0, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 1 back to default pos
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
    leg_group_2_command(-42, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 2 down
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
    leg_group_2_command(0, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 2 back to default pos
    leg_group_1_command(-42, 85, 85);                                                    // leg group 1 fowrd and up
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
    leg_group_1_command(-42, default_standing_femur_angle, default_standing_tibia_angle);  //leg group 1 down
  }
}
 
void rotate_left(int reference_time_millis, int servo_speed) {
 
  //  int default_standing_coxa_angle = 0;
  //int default_standing_femur_angle = 5;
  //int default_standing_tibia_angle = 85;
 
  //total seperate actions preformed
  int action_group_count = 4;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
    //leg_group_2_command (-45, -45, 85); //leg group 2 forward and up
    //leg_group_1_command (0, -5, 85);  //leg group 1 back to default pos
 
    specific_leg_relative_command(1, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(2, -40, 45, 5);
 
    specific_leg_relative_command(3, 40, 45, 5);
 
    specific_leg_relative_command(4, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(5, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(6, -40, 45, 5);
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
 
    specific_leg_relative_command(2, -40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(3, 40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(6, -40, default_standing_femur_angle, default_standing_tibia_angle);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
    //leg_group_1_command (-45, -45, 85); // leg group 1 fowrd and up
 
    specific_leg_relative_command(1, 40, 45, 5);
 
    specific_leg_relative_command(2, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(3, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(4, -40, 45, 5);
 
    specific_leg_relative_command(5, 40, 45, 5);
 
    specific_leg_relative_command(6, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
    //leg_group_1_command (-45, -5, 85); //leg group 1 down
 
    specific_leg_relative_command(1, 40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(4, -40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(5, 40, default_standing_femur_angle, default_standing_tibia_angle);
  }
}
 
void rotate_right(int reference_time_millis, int servo_speed) {
  //  int default_standing_coxa_angle = 0;
  //int default_standing_femur_angle = 5;
  //int default_standing_tibia_angle = 85;
 
  //total seperate actions preformed
  int action_group_count = 4;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
    //leg_group_2_command (-45, -45, 85); //leg group 2 forward and up
    //leg_group_1_command (0, -5, 85);  //leg group 1 back to default pos
 
    specific_leg_relative_command(1, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(2, 40, 45, 5);
 
    specific_leg_relative_command(3, -40, 45, 5);
 
    specific_leg_relative_command(4, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(5, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(6, 40, 45, 5);
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
    //leg_group_2_command (-45, -5, 85); //leg group 2 down
 
    specific_leg_relative_command(2, 40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(3, -40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(6, 40, default_standing_femur_angle, default_standing_tibia_angle);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
    //leg_group_1_command (-45, -45, 85); // leg group 1 fowrd and up
 
    specific_leg_relative_command(1, -40, 45, 5);
 
    specific_leg_relative_command(2, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(3, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(4, 40, 45, 5);
 
    specific_leg_relative_command(5, -40, 45, 5);
 
    specific_leg_relative_command(6, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
    //leg_group_1_command (-45, -5, 85); //leg group 1 down
 
    specific_leg_relative_command(1, -40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(4, 40, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(5, -40, default_standing_femur_angle, default_standing_tibia_angle);
  }
}
 
void strafe_right(int reference_time_millis, int servo_speed) {
 
  //  int default_standing_coxa_angle = 0;
  //int default_standing_femur_angle = 5;
  //int default_standing_tibia_angle = 85;
 
  //total seperate actions preformed
  int action_group_count = 6;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
 
    specific_leg_relative_command(1, -45, 45, 70);
 
    specific_leg_relative_command(2, -45, -10, 110);
 
    specific_leg_relative_command(3, 0, -55, 10);
 
    specific_leg_relative_command(4, 0, 45, 90);
 
    specific_leg_relative_command(5, 45, 45, 70);
 
    specific_leg_relative_command(6, 45, -10, 110);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
 
    specific_leg_relative_command(1, -45, 45, 90);
 
    specific_leg_relative_command(2, -45, -10, 20);
 
    specific_leg_relative_command(3, 0, -50, 110);
 
    specific_leg_relative_command(4, 0, 45, 90);
 
    specific_leg_relative_command(5, 45, 45, 90);
 
    specific_leg_relative_command(6, 45, -10, 20);
 
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
 
    specific_leg_relative_command(1, -45, -55, 10);
 
    specific_leg_relative_command(2, -45, -10, 55);
 
    specific_leg_relative_command(3, 0, -10, 130);
 
    specific_leg_relative_command(4, 0, -10, 110);
 
    specific_leg_relative_command(5, 45, -55, 10);
 
    specific_leg_relative_command(6, 45, -10, 55);
 
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
 
 
    specific_leg_relative_command(1, -45, -55, 10);
 
    specific_leg_relative_command(2, -45, 70, 110);
 
    specific_leg_relative_command(3, 0, 45, 70);
 
    specific_leg_relative_command(4, 0, -10, 90);
 
    specific_leg_relative_command(5, 45, -55, 10);
 
    specific_leg_relative_command(6, 45, 70, 110);
 
  }
 
  else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 4 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 5) {
    //ACTION GROUP 5
 
    specific_leg_relative_command(1, -45, -10, 110);
 
    specific_leg_relative_command(2, -45, 70, 110);
 
    specific_leg_relative_command(3, 0, 45, 70);
 
    specific_leg_relative_command(4, 0, -10, 30);
 
    specific_leg_relative_command(5, 45, -10, 110);
 
    specific_leg_relative_command(6, 45, 70, 110);
 
  }
 
  else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 5 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 6) {
    //ACTION GROUP 6
 
    specific_leg_relative_command(1, -45, -10, 110);
 
    specific_leg_relative_command(2, -45, -10, 110);
 
    specific_leg_relative_command(3, 0, -55, 10);
 
    specific_leg_relative_command(4, 0, -10, 30);
 
    specific_leg_relative_command(5, 45, -10, 110);
 
    specific_leg_relative_command(6, 45, -10, 110);
  }
}
 
 
void strafe_left(int reference_time_millis, int servo_speed) {
 
  //  int default_standing_coxa_angle = 0;
  //int default_standing_femur_angle = 5;
  //int default_standing_tibia_angle = 85;
 
  //total seperate actions preformed
  int action_group_count = 6;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
 
    specific_leg_relative_command(2, -45, 45, 70);
 
    specific_leg_relative_command(1, -45, -10, 110);
 
    specific_leg_relative_command(4, 0, -55, 10);
 
    specific_leg_relative_command(3, 0, 45, 90);
 
    specific_leg_relative_command(6, 45, 45, 70);
 
    specific_leg_relative_command(5, 45, -10, 110);
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
 
    specific_leg_relative_command(2, -45, 45, 90);
 
    specific_leg_relative_command(1, -45, -10, 20);
 
    specific_leg_relative_command(4, 0, -50, 110);
 
    specific_leg_relative_command(3, 0, 45, 90);
 
    specific_leg_relative_command(6, 45, 45, 90);
 
    specific_leg_relative_command(5, 45, -10, 20);
 
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 2 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 3) {
    //ACTION GROUP 3
 
    specific_leg_relative_command(2, -45, -55, 10);
 
    specific_leg_relative_command(1, -45, -10, 55);
 
    specific_leg_relative_command(4, 0, -10, 130);
 
    specific_leg_relative_command(3, 0, -10, 110);
 
    specific_leg_relative_command(6, 45, -55, 10);
 
    specific_leg_relative_command(5, 45, -10, 55);
 
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 3 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 4) {
    //ACTION GROUP 4
 
 
    specific_leg_relative_command(2, -45, -55, 10);
 
    specific_leg_relative_command(1, -45, 70, 110);
 
    specific_leg_relative_command(4, 0, 45, 70);
 
    specific_leg_relative_command(3, 0, -10, 90);
 
    specific_leg_relative_command(6, 45, -55, 10);
 
    specific_leg_relative_command(5, 45, 70, 110);
 
  }
 
  else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 4 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 5) {
    //ACTION GROUP 5
 
    specific_leg_relative_command(2, -45, -10, 110);
 
    specific_leg_relative_command(1, -45, 70, 110);
 
    specific_leg_relative_command(4, 0, 45, 70);
 
    specific_leg_relative_command(3, 0, -10, 30);
 
    specific_leg_relative_command(6, 45, -10, 110);
 
    specific_leg_relative_command(5, 45, 70, 110);
 
  }
 
  else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 5 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 6) {
    //ACTION GROUP 6
 
    specific_leg_relative_command(2, -45, -10, 110);
 
    specific_leg_relative_command(1, -45, -10, 110);
 
    specific_leg_relative_command(4, 0, -55, 10);
 
    specific_leg_relative_command(3, 0, -10, 30);
 
    specific_leg_relative_command(6, 45, -10, 110);
 
    specific_leg_relative_command(5, 45, -10, 110);
  }
}
 
 
void greeting(int reference_time_millis, int servo_speed) {
  //  int default_standing_coxa_angle = 0;
  //int default_standing_femur_angle = 5;
  //int default_standing_tibia_angle = 85;
 
  //total seperate actions preformed
  int action_group_count = 2;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
 
    specific_leg_relative_command(1, -45, default_standing_femur_angle, -90);
 
    specific_leg_relative_command(2, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(3, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(4, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(5, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
    specific_leg_relative_command(6, default_standing_coxa_angle, default_standing_femur_angle, default_standing_tibia_angle);
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
 
    specific_leg_relative_command(1, -45, default_standing_femur_angle, -20);
  }
}
 
void dance(int reference_time_millis, int servo_speed) {
 
  //total seperate actions preformed
  int action_group_count = 2;
 
  //add time to movement cycle to allow for movement speed slower than expected (respects a min value)
  int added_time_to_movement_cycle = servo_speed * 2;
  if (added_time_to_movement_cycle < added_time_to_movement_cycle_min) {
    added_time_to_movement_cycle = added_time_to_movement_cycle_min;
  }
  //how long it take to repeat the action (ms)
  int movement_cycle_period = servo_speed * action_group_count + added_time_to_movement_cycle;
 
  //how many ms have elapsed during the current movemnt cycle
  int current_movement_cycle_elapsed = (millis() - reference_time_millis) % movement_cycle_period;
 
  //determine which action group to do based on how much time has elapsed in the period of the movement cycle
  if (current_movement_cycle_elapsed < movement_cycle_period / action_group_count) {
    //ACTION GROUP 1
 
    specific_leg_relative_command(1, -45, 0, 130);
 
    specific_leg_relative_command(2, -45, 0, 40);
 
    specific_leg_relative_command(3, 0, 45, -45);
 
    specific_leg_relative_command(4, 0, 45, -45);
 
    specific_leg_relative_command(5, 45, 0, 130);
 
    specific_leg_relative_command(6, 45, 0, 40);
 
 
  } else if (current_movement_cycle_elapsed > (movement_cycle_period / action_group_count) * 1 && current_movement_cycle_elapsed < (movement_cycle_period / action_group_count) * 2) {
    //ACTION GROUP 2
    //
 
    specific_leg_relative_command(1, -45, 0, 40);
 
    specific_leg_relative_command(2, -45, 0, 130);
 
    specific_leg_relative_command(3, 0, 0, -90);
 
    specific_leg_relative_command(4, 0, 0, -90);
 
    specific_leg_relative_command(5, 45, 0, 40);
 
    specific_leg_relative_command(6, 45, 0, 130);
  }
}




3D Model & Casing

This 3D model represents an open-source, 3D-printed, low-cost hexapod designed as an expandable and durable robotics platform.

Download: STL Files

It is crafted to be lightweight and compact, making it suitable for various applications.

The hexapod integrates a First-Person View (FPV) camera that provides real-time video feedback, enhancing its usability in remote or visually restrictive environments.

The design features a semi-modular electronic mounting system, allowing for straightforward upgrades and maintenance. Assembly of the robot is efficient, requiring minimal screws, with most only necessary for securing the servo heads. This project emphasizes a minimal electronics approach, aiming to keep the construction and maintenance process as simple as possible while ensuring reliability and functionality.

Hexapod Robot using ESP32-CAM



Android App for Robot Control

For controlling the DIY Spiderbot Hexapod Robot, we will use an Android App. The App has been designed using MIT App Inventor.

The Android App can be downloaded from the following links:

Download: SpiderBot Android App

First turn on the spider bot, and use your wifi settings to find and connect to the wifi access point created by ESP32CAM.

  • Live Feed Area: The top portion shows a live video feed, which is from the robot’s camera (ESP32-CAM).
  • Directional Control Buttons: There are buttons labeled “Forward,” “Reverse,” “Rotate Left,” “Rotate Right,” “Strafe Left,” and “Strafe Right.” These are for moving the robot in various directions.
  • Action Buttons: There’s a “Stop” button, which immediately halts any motion, and a “Calibrate Servos” button, which can be used to reset or calibrate the robot’s servos.
  • Head Light Toggle: Toggles the 2W LED for night vision.
  • Status/Settings Area: At the bottom, there’s an input field for “IP/Port,” where the user can specify the network address to connect to the robot. There are also “Say Hi!” and “Dance” buttons, which trigger predefined animations or movements.
  • Stream Control: A “Refresh Stream” button is used to refresh the video feed.

The Android app is also available as an MIT app inventor project which allows you to customize the app using the free online Android app development suite [MIT app inventor]



Robot Testing & FOV Video Streaming

After the assembly and code upload part is complete, the DIY Spiderbot Hexapod Robot with ESP32-CAM is ready for testing.

Hexapod Robot using ESP32-CAM

The Robot can perform complex maneuvers such as turning and climbing as well as adjusting the height.

The video streaming can be observed on the Android App.


Video Tutorial with Assembly, Testing, & Much More

Spiderbot Open Source FPV Hexapod Robotics Platform - Build Your Own!
Watch this video on YouTube.

Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Reddit Telegram WhatsApp
Previous ArticleDesign for Manufacturing (DFM) – PCB Design & Analysis
Next Article DIY Thermal Imaging Camera with MLX90640 & Raspberry Pi

Related Posts

IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

Updated:May 10, 20261K
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

Updated:May 2, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

Updated:April 27, 20261K
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

Updated:April 27, 20262K
DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

Updated:February 1, 20261K
Add A Comment

CommentsCancel reply

Latest Posts
IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

May 31, 2026
DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

May 10, 2026
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

May 2, 2026
A Guide to Sourcing Obsolete ICs for Vintage Projects

Beyond AliExpress: A Guide to Sourcing Obsolete ICs for Vintage Projects

April 21, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

April 27, 2026
Building a Smart Sensor Node with a BLE Microcontroller

Building a Smart Sensor Node with a BLE Microcontroller

February 26, 2026
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

April 27, 2026
DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

February 1, 2026
Top Posts & Pages
  • 12V DC to 220V AC Inverter Circuit & PCB
    12V DC to 220V AC Inverter Circuit & PCB
  • IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
    IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
  • IoT Based PM & Air Quality Monitoring System using ESP32
    IoT Based PM & Air Quality Monitoring System using ESP32
  • ECG Graph Monitoring with AD8232 ECG Sensor & Arduino
    ECG Graph Monitoring with AD8232 ECG Sensor & Arduino
  • Buck Converter: Basics, Working, Design & Application
    Buck Converter: Basics, Working, Design & Application
  • How to use INA226 DC Current Sensor with Arduino
    How to use INA226 DC Current Sensor with Arduino
  • Pulse Rate (BPM) Monitor using Arduino & Pulse Sensor
    Pulse Rate (BPM) Monitor using Arduino & Pulse Sensor
  • ESP32 CAN Bus Tutorial | Interfacing MCP2515 CAN Module with ESP32
    ESP32 CAN Bus Tutorial | Interfacing MCP2515 CAN Module with ESP32
Categories
  • Arduino Projects (197)
  • Articles (60)
    • Learn Electronics (19)
    • Product Review (15)
    • Tech Articles (28)
  • Electronics Circuits (46)
    • 555 Timer Projects (21)
    • Op-Amp Circuits (7)
    • Power Electronics (13)
  • IoT Projects (204)
    • ESP32 MicroPython (7)
    • ESP32 Projects (81)
    • ESP32-CAM Projects (15)
    • ESP8266 Projects (76)
    • LoRa/LoRaWAN Projects (22)
  • Microcontrollers (38)
    • AMB82-Mini IoT AI Camera (4)
    • BLE Projects (18)
    • STM32 Projects (19)
  • Raspberry Pi (93)
    • Raspberry Pi Pico Projects (57)
    • Raspberry Pi Pico W Projects (12)
    • Raspberry Pi Projects (24)
Follow Us
  • Facebook
  • Twitter
  • Pinterest
  • Instagram
  • YouTube
About Us

“‘How to Electronics’ is a vibrant community for electronics enthusiasts and professionals. We deliver latest insights in areas such as Embedded Systems, Power Electronics, AI, IoT, and Robotics. Our goal is to stimulate innovation and provide practical solutions for students, organizations, and industries. Join us to transform learning into a joyful journey of discovery and innovation.

Copyright © How To Electronics. All rights reserved.
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us

Type above and press Enter to search. Press Esc to cancel.

Ad Blocker Enabled!
Ad Blocker Enabled!
Looks like you're using an ad blocker. Please allow ads on our site. We rely on advertising to help fund our site.