-
Boards Available from PCBWay.
10/05/2023 at 19:58 • 0 commentsThe bare PCB's for the $10Robot! are now available from PCBWay.
There's also a $5 Discount for new users.
Here are the links:
-
ESP-NOW Controller
10/04/2023 at 13:11 • 2 commentsI heard about the ESP-NOW wireless protocol a while ago and I thought a custom controller would make a good addition to this project. This will not be included in the original budget of $10, but it will still be very cheap to make at around $5, and much cheaper than buying a PlayStation (or other) controller.
Here's the first iteration. It uses the 30 pin ESP Devkit, as this was the cheapest option if you buy them in packs. It has a joystick with 2 analogue axis and 9 buttons, It's powered by 2 AAA batteries.
I wanted to use MicroPython, but ESP-NOW is not included in the main MicroPython firmware. You have to use the nightly build, you can download it here: MicroPython_ESP32
Make sure you download the nightly build:
You can upload the new firmware to your ESP32 device using Thonny (as detailed in a previous log).
Once you have the firmware installed, you need to get the MAC address of the receiving device, in this case our robot. Run the following code on the ESP32 device in your $10Robot!
import network import ubinascii wlan_sta = network.WLAN(network.STA_IF) wlan_sta.active(True) # https://stackoverflow.com/questions/71902740/how-to-retrieve-and-format-wifi-mac-address-in-micropython-on-esp32 print(ubinascii.hexlify(wlan_sta.config('mac'),':').decode().upper())
You should see the MAC address in the output window.
Make a note of the MAC address as you'll need it in your controller code. It's also worth writing it on a little sticky label and sticking it on the robot.
Now load the following code onto the ESP32 in the controller.
import network import espnow from machine import Pin, ADC from time import sleep Y_Pot = ADC(Pin(36)) Y_Pot.atten(ADC.ATTN_11DB) # 0 - 3.3V range Y_Pot.width(ADC.WIDTH_9BIT) X_Pot = ADC(Pin(39)) X_Pot.atten(ADC.ATTN_11DB) # 0 - 3.3V range X_Pot.width(ADC.WIDTH_9BIT) UP = Pin(5, Pin.IN, Pin.PULL_UP) DOWN = Pin(15, Pin.IN, Pin.PULL_UP) LEFT = Pin(16, Pin.IN, Pin.PULL_UP) RIGHT = Pin(4, Pin.IN, Pin.PULL_UP) L1 = Pin(32, Pin.IN, Pin.PULL_UP) R1 = Pin(23, Pin.IN, Pin.PULL_UP) L2 = Pin(33, Pin.IN, Pin.PULL_UP) START = Pin(14, Pin.IN, Pin.PULL_UP) SELECT = Pin(13, Pin.IN, Pin.PULL_UP) # A WLAN interface must be active to send()/recv() sta = network.WLAN(network.STA_IF) # Or network.AP_IF sta.active(True) #sta.disconnect() # For ESP8266 e = espnow.ESPNow() e.active(True) peer = b'\x0A\x0B\x0C\x0D\x0E\x0F' # MAC address of peer's wifi interface e.add_peer(peer) # Must add_peer() before send() print("Start") while True: Y_Raw = Y_Pot.read() X_Raw = X_Pot.read() Y = int(Y_Raw/2) # Convert to 8bit X = int(X_Raw/2) # Convert to 8bit e.send(peer, b'start', True) e.send(peer, str(Y), True) e.send(peer, str(X), True) e.send(peer, str(UP.value()), True) e.send(peer, str(DOWN.value()), True) e.send(peer, str(LEFT.value()), True) e.send(peer, str(RIGHT.value()), True) e.send(peer, str(L1.value()), True) e.send(peer, str(R1.value()), True) e.send(peer, str(L2.value()), True) e.send(peer, str(START.value()), True) e.send(peer, str(SELECT.value()), True)
Remember to change the peer MAC address on line 32 to match the one in your robot.
Now load the following code onto the ESP32 in your robot.
import network import espnow import ubinascii import time from machine import Pin # A WLAN interface must be active to send()/recv() sta = network.WLAN(network.STA_IF) sta.active(True) #sta.disconnect() # Because ESP8266 auto-connects to last Access Point e = espnow.ESPNow() e.active(True) # Set up motors M1a = Pin(18, Pin.OUT) M1b = Pin(19, Pin.OUT) M2a = Pin(25, Pin.OUT) M2b = Pin(26, Pin.OUT) data =[0,0,0,0,0,0,0,0,0,0,0,0] # List to store controller data count = 0 UP = 0 DOWN = 0 LEFT = 0 RIGHT = 0 print("Press CTRL-C now to stop program") time.sleep(2) print("Start") while True: host, msg = e.recv() if msg: # msg == None if timeout in recv() if msg == b'start': # Look for the start of the message and reset the count count = 0 if count != 0: data[count] = int(msg) # Load the message data into the list count +=1 if count == 12: # End of message count = 0 #Uncomment the following line to show all data from the controller #print('{:03d}'.format(data[1]),'{:03d}'.format(data[2]),data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11]) if data[3] == 0 and UP == 0: UP = 1 print("UP Pressed") M1a.value(0) M1b.value(1) M2a.value(0) M2b.value(1) elif data[3] == 1 and UP == 1: UP = 0 print("UP Released") M1a.value(0) M1b.value(0) M2a.value(0) M2b.value(0) if data[4] == 0 and DOWN == 0: DOWN = 1 print("DOWN Pressed") M1a.value(1) M1b.value(0) M2a.value(1) M2b.value(0) elif data[4] == 1 and DOWN == 1: DOWN = 0 print("DOWN Released") M1a.value(0) M1b.value(0) M2a.value(0) M2b.value(0) if data[5] == 0 and LEFT == 0: LEFT = 1 print("LEFT Pressed") M1a.value(1) M1b.value(0) M2a.value(0) M2b.value(1) elif data[5] == 1 and LEFT == 1: LEFT = 0 print("LEFT Released") M1a.value(0) M1b.value(0) M2a.value(0) M2b.value(0) if data[6] == 0 and RIGHT == 0: RIGHT = 1 print("RIGHT Pressed") M1a.value(0) M1b.value(1) M2a.value(1) M2b.value(0) elif data[6] == 1 and RIGHT == 1: RIGHT = 0 print("RIGHT Released") M1a.value(0) M1b.value(0) M2a.value(0) M2b.value(0)
If all goes well, you should be able to drive your robot!
This was just a simple test to see if it worked. I was very pleased it was just as responsive as the PlayStation controller. It had a range of about 20 metres. I'll add some more code soon, using the joystick with analogue speed control.
-
PS3/4 Controller
10/03/2023 at 10:36 • 0 commentsWe already use Playstation controllers for the other robots that we use in our club, there's also a prety good chance that the students will have them at home. So It makes sense to use them with the $10Robot! as well.
A quick bit of googling lead me to an Arduino library: PS4-esp32 (there's also a library available for PS3 controllers).
The first thing you have to do it get the MAC address of your controller. You can do this by using the Sixaxis Pair Tool, I used this one. Just install it then plug in your controller with a USB cable and it will show you the MAC address. Make a note of it as you'll need it in your Arduino program.
After installing the Arduino library, it was pretty easy to get up and running, as there are a number of example programs included. I used PS4ReceiveData as my starting point. It doesn't control a robot yet, it just shows the button presses in the serial monitor. I modified the original code a little, just so that it makes the serial output a bit easier to read.
#include <PS4Controller.h> //Set up variables int LX = 0; int LY = 0; int R_X = 0; int RY = 0; int LX_old = 0; int LY_old = 0; int RX_old = 0; int RY_old = 0; int deadBand = 8; bool connected = 0; void setup() { Serial.begin(115200); PS4.begin("0a:0b:0c:0d:oe:0f"); // Enter your MAC address here. Serial.println("Ready."); delay(200); } void loop() { if ((PS4.isConnected()) && (connected != 1)) { Serial.println("Connected!"); Serial.printf("Battery Level : %d\n", PS4.Battery()); connected = 1; } // Below has all accessible outputs from the controller if (PS4.isConnected()) { if (PS4.Right()) Serial.println("Right Button"); if (PS4.Down()) Serial.println("Down Button"); if (PS4.Up()) Serial.println("Up Button"); if (PS4.Left()) Serial.println("Left Button"); if (PS4.Square()) Serial.println("Square Button"); if (PS4.Cross()) Serial.println("Cross Button"); if (PS4.Circle()) Serial.println("Circle Button"); if (PS4.Triangle()) Serial.println("Triangle Button"); if (PS4.UpRight()) Serial.println("Up Right"); if (PS4.DownRight()) Serial.println("Down Right"); if (PS4.UpLeft()) Serial.println("Up Left"); if (PS4.DownLeft()) Serial.println("Down Left"); if (PS4.L1()) Serial.println("L1 Button"); if (PS4.R1()) Serial.println("R1 Button"); if (PS4.Share()) Serial.println("Share Button"); if (PS4.Options()) Serial.println("Options Button"); if (PS4.L3()) Serial.println("L3 Button"); if (PS4.R3()) Serial.println("R3 Button"); if (PS4.PSButton()) Serial.println("PS Button"); if (PS4.Touchpad()) Serial.println("Touch Pad Button"); if (PS4.L2()) { Serial.printf("L2 button at %d\n", PS4.L2Value()); } if (PS4.R2()) { Serial.printf("R2 button at %d\n", PS4.R2Value()); } if (PS4.LStickX()) { LX = PS4.LStickX(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((LX < deadBand) && (LX > -deadBand)) { LX = 0; } } if (PS4.LStickY()) { LY = PS4.LStickY(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((LY < deadBand) && (LY > -deadBand)) { LY = 0; } } if (PS4.RStickX()) { R_X = PS4.RStickX(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((R_X < deadBand) && (R_X > -deadBand)) { R_X = 0; } } if (PS4.RStickY()) { RY = PS4.RStickY(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((RY < deadBand) && (RY > -deadBand)) { RY = 0; } } // Only print the values if they have changed if ((LX != LX_old) || (LY != LY_old) || (R_X != RX_old) || (RY != RY_old)) { Serial.printf("LX = %04d, LY = %04d, RX = %04d, RY = %04d\n", LX, LY, R_X, RY); } LX_old = LX; LY_old = LY; RX_old = R_X; RY_old = RY; if (PS4.Charging()) Serial.println("The controller is charging"); if (PS4.Audio()) Serial.println("The controller has headphones attached"); if (PS4.Mic()) Serial.println("The controller has a mic attached"); } }
Here's the serial output:
Here's the full modified code to control the $10Robot!:
#include <PS4Controller.h> //Set up variables int LX = 0; int LY = 0; int R_X = 0; int RY = 0; int LX_old = 0; int LY_old = 0; int RX_old = 0; int RY_old = 0; int deadBand = 8; bool connected = 0; bool up = 0; bool down = 0; bool left = 0; bool right = 0; void setup() { Serial.begin(115200); PS4.begin("b8:27:eb:23:65:fd"); //ESP32 pin setup pinMode(2, OUTPUT); // LED pinMode(18, OUTPUT); // Motor 1a pinMode(19, OUTPUT); // Motor 1b pinMode(25, OUTPUT); // Motor 2a pinMode(26, OUTPUT); // Motor 2b Serial.println("Ready."); } void loop() { if ((PS4.isConnected()) && (connected != 1)) { Serial.println("Connected!"); Serial.printf("Battery Level : %d\n", PS4.Battery()); connected = 1; } // Below has all accessible outputs from the controller if (PS4.isConnected()) { if (PS4.Right()) { if (right == 0) { Serial.println("Right Pressed"); Serial.println("Turn Right"); } right = 1; digitalWrite(18, 0); digitalWrite(19, 1); digitalWrite(25, 1); digitalWrite(26, 0); } else if (right == 1) { right = 0; Serial.println("Right Released"); Serial.println("STOP"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } if (PS4.Down()) { if (up == 0) { Serial.println("Down Pressed"); Serial.println("Backwards"); } down = 1; digitalWrite(18, 1); digitalWrite(19, 0); digitalWrite(25, 1); digitalWrite(26, 0); } else if (down == 1) { down = 0; Serial.println("Down Released"); Serial.println("STOP"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } if (PS4.Up()) { if (up == 0) { Serial.println("Up Pressed"); Serial.println("Forwards"); } up = 1; digitalWrite(18, 0); digitalWrite(19, 1); digitalWrite(25, 0); digitalWrite(26, 1); } else if (up == 1) { up = 0; Serial.println("Up Released"); Serial.println("STOP"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } if (PS4.Left()) { if (left == 0) { Serial.println("Left Pressed"); Serial.println("Turn Left"); } left = 1; digitalWrite(18, 1); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 1); } else if (left == 1) { left = 0; Serial.println("Left Released"); Serial.println("STOP"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } if (PS4.Square()) Serial.println("Square Button"); if (PS4.Cross()) Serial.println("Cross Button"); if (PS4.Circle()) Serial.println("Circle Button"); if (PS4.Triangle()) Serial.println("Triangle Button"); if (PS4.UpRight()) Serial.println("Up Right"); if (PS4.DownRight()) Serial.println("Down Right"); if (PS4.UpLeft()) Serial.println("Up Left"); if (PS4.DownLeft()) Serial.println("Down Left"); if (PS4.L1()) Serial.println("L1 Button"); if (PS4.R1()) Serial.println("R1 Button"); if (PS4.Share()) Serial.println("Share Button"); if (PS4.Options()) Serial.println("Options Button"); if (PS4.L3()) Serial.println("L3 Button"); if (PS4.R3()) Serial.println("R3 Button"); if (PS4.PSButton()) Serial.println("PS Button"); if (PS4.Touchpad()) Serial.println("Touch Pad Button"); if (PS4.L2()) { Serial.printf("L2 button at %d\n", PS4.L2Value()); } if (PS4.R2()) { Serial.printf("R2 button at %d\n", PS4.R2Value()); } if (PS4.LStickX()) { LX = PS4.LStickX(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((LX < deadBand) && (LX > -deadBand)) { LX = 0; } } if (PS4.LStickY()) { LY = PS4.LStickY(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((LY < deadBand) && (LY > -deadBand)) { LY = 0; } } if (PS4.RStickX()) { R_X = PS4.RStickX(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((R_X < deadBand) && (R_X > -deadBand)) { R_X = 0; } } if (PS4.RStickY()) { RY = PS4.RStickY(); //Serial.printf("Left Stick x at %d\n", PS4.LStickX()); // Use the deadband to make sure the value goes back to zero when you let go of the stick. if ((RY < deadBand) && (RY > -deadBand)) { RY = 0; } } // Only print the values if they have changed if ((LX != LX_old) || (LY != LY_old) || (R_X != RX_old) || (RY != RY_old)) { Serial.printf("LX = %04d, LY = %04d, RX = %04d, RY = %04d\n", LX, LY, R_X, RY); } LX_old = LX; LY_old = LY; RX_old = R_X; RY_old = RY; if (PS4.Charging()) Serial.println("The controller is charging"); if (PS4.Audio()) Serial.println("The controller has headphones attached"); if (PS4.Mic()) Serial.println("The controller has a mic attached"); } }
This just uses the DPAD buttons to control the robot as it's an easy starting point. I'll add code to use the joysticks with speed control at a later date.
-
Create an App with Blynk
09/29/2023 at 15:16 • 0 commentsNow it's time to get programming. When introducing young students to programming, you'd normally start by blinking an LED, so let's do that but we'll do it controlled over the internet by an app on your phone. Then we'll make the app control your robot!
To do this we'll use blynk.io
Blynk is a low code Internet of Things framework. It works very well with the ESP32 and Arduino. There's a free plan with enough features to create our app for controlling a robot.
Blynk has got some great examples to get you going, we'll use one of those but we'll modify it for our needs. After you have created a free account, go to you dashboard then click on Templates on the left hand side, then click on + New Template.
Give your new template a name, I called mine 'Robot Controller'. The hardware should be ESP32 and the connection should be WiFi.
Click on Datastreams, then + New Datastream.
Select Virtual Pin, then click on Create.
Repeate this 6 more times, your datastreams should look like this:
Click on save in the top right corner.
Click on the lifebuoy icon in the bottom left of the screen, then select Quickstart.
Click on Let's Go! Then select ESP32 as your hardware. Follow the instructions to install the Blynk library for Arduino.
You'll be presented with some example code, but we are going to use a different example.
Click on the other examples at the bottom of the screen.
Select the Blynk Blink example from the drop down box on the Left.
Also change the Template Name to match what you called the template you created earlier.
Now click on Copy Example on the right hand side of the screen.
Open the Arduino IDE and paste in the copied sketch.
Change the WiFi credentials to match your own network.
We'll now add the code to flash an LED. At the bottom of the void setup() section of code, add the following line:
pinMode(2, OUTPUT); // LED
So it should look like this:
Now just after your WiFi credentials, add the following code:
BLYNK_WRITE(V0) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("LED On"); digitalWrite(2, HIGH); } if (value== 0) { Serial.println("LED Off"); digitalWrite(2, LOW); } // Update state Blynk.virtualWrite(V1, value); }
It should look like this:
Upload the code to your ESP32 device. Turn on the Serial Monitor and change the Baud Rate to 115200. If everything is correct it should connect to your WiFI and you should see some output like this:
Now open the Blynk app on your phone. This has got a drag and drop interface to easily create your app. Click on the + symbol at the top right of the screen to Add a New Device. Then select Manually from template.
Select the template you created earlier, then click on Create. Now click on the spanner icon at the bottom of the screen to enter Developer Mode.
Now you can start adding buttons and other controls for your app. Click on the screen, then from the menu that appears, add a Button. Repeat the process to add an LED. Long press on the items you have just added and drag them around the screen to reposition them.
Arrange the button and LED like this:
Tap on the button to change it's properties. Tap on Datastreams then select the entry at the top of the list, this should be IntegerV0.
Next change the Mode to Switch, as shown here:
Next, tap on the LED and change it's Datastream to IntegerV1. Tap the back arrow at the top of the screen to exit Developer Mode.
If everything has worked correctly, when you tap the button on the screen, the LED on the ESP32 should light up!
You should also see the output change in the Serial Monitor window.
Now we have our LED flashing, it's time to make our robot move.
In the Blynk app on your mobile, select your Robot Controller App. Enter developer mode by tapping the spanner at the bottom of the screen. Add 5 more buttons in a cross pattern, then assign the Datastreams to the virtual pins as shown:
You can rename the buttons by tapping the Design tab at the bottom of the screen when you are editing the button's settings.
That's the app finished! We now need to modify our Arduino code. In the Arduino IDE, add the following code to Void setup() to create 4 output pins which will control the motors:
pinMode(18, OUTPUT); // Motor 1a pinMode(19, OUTPUT); // Motor 1b pinMode(25, OUTPUT); // Motor 2a pinMode(26, OUTPUT); // Motor 2b
We now need to add some code to tell the motors which way to turn. These section of code are linked to the virtual pins we set up in the app. So to make the robot move forward, add the following code:
//Forwards BLYNK_WRITE(V2) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Forward"); digitalWrite(18, 0); digitalWrite(19, 1); digitalWrite(25, 0); digitalWrite(26, 1); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } }
This is linked to virtual pin V2, so when you press the UP button in the app, the motors rotate forwards.
We now just need to add the code for the other buttons, to make the robot go backwards and turn left and right.
Here's the full code, remember to change your WiFi credentials and add your own BLYNK_TEMPLATE_ID and BLYNK_AUTH_TOKEN.
/************************************************************* You’ll need: - Blynk IoT app (download from App Store or Google Play) - ESP32 board - Decide how to connect to Blynk (USB, Ethernet, Wi-Fi, Bluetooth, ...) There is a bunch of great example sketches included to show you how to get started. Think of them as LEGO bricks and combine them as you wish. For example, take the Ethernet Shield sketch and combine it with the Servo example, or choose a USB sketch and add a code from SendData example. *************************************************************/ /* Fill-in information from Blynk Device Info here */ #define BLYNK_TEMPLATE_ID "Your Template ID" #define BLYNK_TEMPLATE_NAME "Robot Control Pad" #define BLYNK_AUTH_TOKEN "Your Auth Token" /* Comment this out to disable prints and save space */ #define BLYNK_PRINT Serial #include <WiFi.h> #include <WiFiClient.h> #include <BlynkSimpleEsp32.h> // Your WiFi credentials. // Set password to "" for open networks. char ssid[] = "Your SSID"; char pass[] = "YourPassword"; BLYNK_WRITE(V0) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("LED On"); digitalWrite(2, HIGH); } if (value== 0) { Serial.println("LED Off"); digitalWrite(2, LOW); } // Update state Blynk.virtualWrite(V1, value); } //Forwards BLYNK_WRITE(V2) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Forwards"); digitalWrite(18, 0); digitalWrite(19, 1); digitalWrite(25, 0); digitalWrite(26, 1); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } } //Backwards BLYNK_WRITE(V3) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Backwards"); digitalWrite(18, 1); digitalWrite(19, 0); digitalWrite(25, 1); digitalWrite(26, 0); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } } //Left BLYNK_WRITE(V4) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Left"); digitalWrite(18, 1); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 1); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } } //Right BLYNK_WRITE(V5) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Right"); digitalWrite(18, 0); digitalWrite(19, 1); digitalWrite(25, 1); digitalWrite(26, 0); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } } //Stop BLYNK_WRITE(V6) { // Set incoming value from pin V0 to a variable int value = param.asInt(); if (value== 1) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } if (value== 0) { Serial.println("Stop"); digitalWrite(18, 0); digitalWrite(19, 0); digitalWrite(25, 0); digitalWrite(26, 0); } } void setup() { // Debug console Serial.begin(115200); Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); // You can also specify server: //Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, "blynk.cloud", 80); //Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, IPAddress(192,168,1,100), 8080); pinMode(2, OUTPUT); // LED pinMode(18, OUTPUT); // Motor 1a pinMode(19, OUTPUT); // Motor 1b pinMode(25, OUTPUT); // Motor 2a pinMode(26, OUTPUT); // Motor 2b } void loop() { Blynk.run(); // You can inject your own code or combine it with other sketches. // Check other examples on how to communicate with Blynk. Remember // to avoid delay() function! }
Here's a quick video of the app controlling the robot. Check out the instructions section to learn how to put the robot together (coming soon!).
-
TPU Tracks
09/28/2023 at 13:59 • 0 commentsA s mentioned in my previous log, my chassis design was based around some cheap tank tracks I found on Aliexpress, these came in at about $2.85 for the pair. I hadn't included these in my original costing so I need a cheaper alternative.
I had a roll of TPU filament lying around but hadn't had much need for it. So this was a perfect opportunity to try it out properly.
I set about designing a version of the tracks in FreeCAD.
It turns out that it actually prints pretty easily, I just used the default settings and they came out well. There's a little bit of stringing on the inside lugs but nothing I can't live with.
Having a quick look on Amazon, I can get a 1kg roll of TPU for around $24. I weighed the tracks and together they came in at 19 grams. That works out at just under $0.5 for the pair. That will get me back on budget.
-
Laser Cut Chassis
09/28/2023 at 13:30 • 0 commentsThe overall size of the chassis was determined by the size of the tracks I could get. I found these Lego compatible ones on aliexpress.
I also wanted to make sure I could fit different types of battery pack inside, mainly a pack of 6 x AA, like this:
AA batteries are cheap and easy to get hold of and there's a good chance that the students will already have some at home that they can use. Hopefully they would have some rechargeable ones, which of course would keep the cost down.
I also wanted the option to use Lipo batteries for more advanced users.
Here's the first laser cut prototype of the chassis.
The top cover has a tool-less quick release mechanism so you have easy access to the electronics inside.
The mounting plate inside has lots of options for different development boards.
Here's my Pi Zero sized board:
And here's the A+ sized board:
The underside with the bottom panel removed with space for 6 x AA's of a 3s Lipo.
-
A+ +
09/28/2023 at 11:47 • 0 commentsAfter researching the different types of ESP32 Devkits I redesigned the A+ sized board. I was hoping to make one board that would accept all the different devkits but in the end I designed two new boards. One to for the 38pin versions and one for the 30pin version.
The 38 pin version was pretty easy as I just added another row of pads so the board could accommodate both the wide and narrow versions.
As there was some space under where the Devkit would go I also added some pads to solder in a capacitor which would help if you are driving more powerful motors.
The pinout for the 30 pin version was quite different so It needed it's own board. I wanted to be able to use this version of the Devkit as it's available in bulk, so it works out to be the cheaper option.
-
WebREPL and Wireless Programming
09/26/2023 at 09:00 • 0 commentsOne of the thing I liked about the M5 Stamp was that you could program them wirelessly with minimal effort. This is a great feature for working with robots as you don't need to keep picking them up to reprogram them. This relied on M5's UiFlow software but I was wondering if there was a way to make this work with generic ESP32 modules.
As it turns out, Hackaday had the answer with this article: Wireless micropython programming with thonny.
I then found out more about WebREPL and ESP's with this article from Adafruit: ESP8266 WebREPL
Some of the details seem to have changed or it's different for the ESP32 as mine didn't automatically create it's own access point, so to create one I followed this guide from Random Nerd Tutorials: ESP32 Access Point
We only need the first part of the code from that tutorial as we don't need to create the Web Page.
Here's a run down of the procedure:
1. Install Thonny, It's a great Python IDE especially for use in education.
2. Upload the Micro Python firmware to your ESP32 board. You can do this from within the Thonny IDE, your board has to be connected via USB. Just click on the info tray at the bottom right of the Thonny window, then click on Configure interpreter.
Next click on Install or update MicroPython (esptool).
Then select your ESP32 board and version from the drop down boxes and finally click on install.
In the Shell window at the bottom of the Thonny screen, type:
import webrepl_setup
Enter E.
Then enter a password (use a better one than I did!).
Then enter y to reboot.
Now enter the following code, then from the file menu select Save As, then select MicroPython Device. Save the file as boot.py (overwrite the exsisting boot.py file).
# This file is executed on every boot (including wake-boot from deepsleep) #import esp #esp.osdebug(None) import time import network import webrepl webrepl.start() #Set up ESP32 as access point ssid = 'MP_AP' password = '123456789' ap = network.WLAN(network.AP_IF); ap.active(True); ap.config(essid=ssid,authmode=network.AUTH_WPA_WPA2_PSK, password=password) print(ap.ifconfig())
Reboot your ESP32 by pressing the EN button on the Devkits or disconnecting then reconnecting the USB cable.
The ESP32 access point should show up in your list of WiFi networks.
In Thonny, select configure Interpreter again.
From the Port or WebREPL drop down box, choose <WebREPL>.
Enter the password that you entered for the WebREPL setup (not the access point password in the Python code!).
Click OK. The shell window in Thonny will now disconnect as it's now trying to connect over WiFi not USB.
Go to your WiFi settings and connect to the MP_AP access point.
It will report that's there's no internet connection, so your computer will go offline (unless you have a wired connection as well).
Click on Stop/Restart backend in Thonny, and the Shell window will reconnect. But this time it will be connected over WiFi, not USB!
It may be a little slower but you can now do everything that you could do over a wired connection.
-
Which ESP?
09/21/2023 at 20:29 • 0 commentsAfter designing the last board with a ESP32 Devkit, I found there were a few different versions with different pinouts.
I bought a some of the different boards to try to work out what was what. This is what I found:
Starting on the left, is the basic ESP32 surface mount module.
Next is the 30pin DevKit, this only seams to come with one choice of ESP32 module with the onboard antenna.
Then we have 2 38pin boards in a narrow format, these are available with different ESP32 modules.
On the right there are 2 38pin boards in a wide format, these are also available with ESP32 modules.
The 38pin boards have the pins in the same order but the different widths means they are not compatible.
The 30pin board has a different pinout. The 8 missing pins are for the Internal flash chip and are not needed.
The 30pin board are available in bulk packs and so are a bit cheaper than the others at about $2.70 each.
I'll see if I can design a board which can accept any of these board for maximum flexibility.
-
A+
09/21/2023 at 20:25 • 0 commentsI originally wanted to board to be the same size as a Pi Zero. This was possible using the basic ESP32 module, but it was lacking a USB port which meant you needed a separate programmer to upload code. I had some of the ESP32 Devkits in my parts box so I set about designing a board using one of these. These boards are quite a bit bigger than the ESP32 modules so this design would have to be bigger than a Pi Zero. I though about designing a slightly bigger board with a different mounting hole pattern but I then decided against this as I wanted these boards to be drop in replacements for the RPi's that we already use in our robots. The next size board I looked at was the Pi A+, this was a pretty good fit for the ESP32 Devkit.
Here's the first board I designed using this shape. It uses the 38pin Wide Devkit as I already had some of these to hand.
A bonus of this sized board is that it breaks out all of the GPIO pins, so you have the option of making more complicated robots.