Control ESP32 from ANYWHERE in the World - Step-By-Step Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone today i'm going to show you how you can control your esp32 microcontroller from any place in the world with the internet connection in an early real time to make it possible i'm gonna have to create a three different projects the first one arduino framework project created with platform io this is going to be the code that will run on the microcontroller and perform requested actions like for example changing the voltage on a digital pin second project will be a react.js application hosted on aws s3 it will run in the browser that's where we'll be able to send the commands from using a nice web interface first project aws api gateway websockets server this will be a central point of communication and will be responsible for passing websocket messages between our clients the arcgis web app and esp32 in terms of hardware i will use esp32 dev keyboard led diode current limiting resistor and some wires to connect everything together as always you can find all the specification in the description below and now let's begin from the implementation so i'm going to open my ide first with visual studio code opened let's start from building platform io project that's gonna be deployed to esp32 microcontroller a websocket client app so i'm gonna start from a clicking on platform io icon the section over here then from the quick access menu open i'm just gonna close this sidebar then on new project name of the project esp32 websockets the board i'm just gonna type nodemcu and gonna select nodemcu32s framework gonna keep arduino and the default location now i'm gonna click on finish and after few seconds the project should be created okay done and now before i start writing code let's think about the main prerequisite for this project which is the wi-fi connection right it is actually internet connection but to get an internet connection we need to connect to wi-fi first and because this part has been already covered in another video esp32 wi-fi connect i'm going to go to my github and just copy paste the project configuration from there essentially just this line which is a monitor speed this is for the serial communication and then i'm gonna go to src and main.cpp i am going to go for the blocking method and that blocking method of connecting to wi-fi is basically that the program is not going to continue until the wi-fi connection is established if you are interested in more details how this code works i really recommend watching esp wi-fi connect video you can find link in the description and by the way don't forget to replace these two values with your actual home wi-fi network name and password otherwise the boat won't be able to connect to the internet okay so now let's add the websocket client code which is gonna go right in there after the print ln connected so that point we should definitely be connected to wi-fi the board should be connected to wi-fi okay so for the websocket client we need to install an external library and i'm gonna do it with the with the help of platform i o again so i'm gonna go to so either you can go back to this icon and uh click on open or just click on the top over there if you had the tab as i had and then two libraries and then search libraries in the search libraries in the search box we want to type websockets then that should be like third or fourth result yes which is this websocket library we want to click on it and add it to the project obviously we need to select the project the one that we are working in in my case this is esp32 websockets and then click on add okay great yeah we can close pio home for now and platform io.ini we're not gonna need it and now first of all i'm gonna have to include this library which is websocket websockets client and i need to create an instance of it right so i need to call it constructor so what i'm gonna type in is web sockets client and let's just call it maybe ws client that should be enough okay the next step right in here after the the println connected i'm going to type ws client dot begin ssl so we're going to establish a secure connection with the websocket server the reason is that aws api gateway web sockets gives us a secure url when once we create the server so we're gonna have to establish a secure connection okay so for that what we need is a host and at this point i don't know yet what that host is going to be i don't know actually i do know the port but let's use a another constant i'm just using constants that i'm going to define at the beginning in a second for the url here we're gonna find out once we have our server okay so let's just call it wsurl the fingerprint for now i'm just going to skip it so i will leave it empty and the protocol that's going to be wss okay okay good yeah let me just copy these values now and define them as constants they will be empty ws host and url and the port will be 443 because that's the default port for wss protocol good okay so that establishes the connection there is one more line of code that needs to be added in the loop which is ws client dot i think it's called loop yeah that's the loop method so that's gonna keep the connection alive and we also need to set if you want to be able to receive any events from this websocket server right through this websocket client we need to call on event method and provide an event handler that i will have to create as a function so let's define a function on ws event maybe then what kind of signature this function should have maybe let's copy this name of the function and pass it to on event and then let's click through on on event we've got websocket client event let's just click through on it okay so yeah that's the signature that we need to copy paste they should be right in there okay so what we've got is a type that's the type of an event that gets triggered at any point in time i guess then we have a payload and the length of that payload payload basically is any information from the event okay so this is like a payload of the event okay let's handle that event with switch case so we will have ws type i think what we've got um yeah maybe let's click through and see what type of okay that's an enum so i'm just going to copy all of these values and let's just handle some of them because we don't need to handle all of them essentially yeah we are interested in disconnected connected text and maybe an error which i'm going to skip for now because i think it might not be even needed i guess unless you want to add some debugging troubleshooting code yeah okay so um maybe let's move connected to to be the first one cool okay um yeah we probably gonna get some we're gonna get them warning uh because we're not handling other types but should be fine right so when the the client connects to the server this event is going to be triggered right so what we gonna see is just an information in the in the serial monitor let's just do something like that and just break then when we disconnect i mean when the board disconnects from the server say it's because the internet connection dropped we're gonna get an information and finally the last one which is the most important this is when the client receives the message from the server okay and when it receives the message from the server this is gonna be the way of controlling esp32 in a real time okay so we're gonna have the other client sending a message to the server and then server passing this message to the other clients let's do serial printf this is going to be useful for the debugging pull process let's just call it ws message and then let's just display the payload okay and break at the end one more step though which is going to be i guess let's create another separate function this is where we're going to handle that payload because when we receive the message we want to read that message and based on the information that is in the message microcontroller is going to perform an action okay which is going to be either a read kind of action or of right kind of action so right reads just gonna give back some information back to the server and then this is gonna be distributed to the other clients or write is gonna actually do something okay so maybe switch on the diode but also i guess it would be nice to just send back some sort of acknowledgement message or like a return status that you know that operation has succeeded so um yeah i think i think this is a right time to create another method another function which is going to be handle message just call it handle message and then we just gonna take this payload which is going to be essentially passed to handle message okay so payload so we will have a different responsibilities here so this is just a listener and this is just gonna this this function is going to be responsible for passing the message and now how we're gonna pass this message so because on the other side there's gonna be react.js application the sort of standard way of communicating in web applications right nowadays is json we can we can say it is json so i think a good format that we can use here could be json and for json we're gonna need to install another external library it's called arduino json so let me just add it to the project again what we have to do is to click on platform io icon then quick access open libraries and in here let's just type json and that's going to be the first one that i'm going to add to project select the project i'm in and click on add and now i don't want to close it yet because there is an example that is very useful so if you click here on that select box and you type parse and then you click on json parser example this is really nice example of how to parse json right because that's go that's going to be the format we expect to get okay so in the handle message function the payload is going to be a message but in a different format just which is okay because we can parse it with arduino json so let's just copy a few fragments of code first of all let's copy this fragment of code which is the include just gonna place it on the top of our main.cpp file and then let's copy this bit which is static json document creation and that's gonna that's gonna be in the handle message it's gonna be initiated there and then finally this fragment starting from deserialization error up to there so this whole if statement okay now i can close pio home and then this is actually what we need one a small modification actually two small modifications the first one is let's use a payload because that needs to be passed to deserialize json this this function basically parse our json and turns it into a document which we can use then to read some data fields from json document and then the static json document this second small modification is on the size the fixed size let's define something like json dock size and let's set it to 2048 should be yeah more than enough cool okay so yeah let me explain what this code does so what we do we essentially create a static json document here then this the reference to that document is passed to deserialize json along with the payload and then the error is the output of this deserialize json function if the error is there so there was an error this is like either incorrect json or the format of json is somehow wrong this uh this error check is going to be true basically and then the the information about the error is printed out in the serial monitor one additional step that we can do is to send an error information back to the server that again is going to be passed to the client so whoever send the incorrect message to our microcontroller is going to get back a message with the error right this is a good thing to have right a two-way sort of communication there's going to be a request and a response how to send an information how to send a message back to the server there is just one method that we need to call and that method is sent t xt where we're gonna we're gonna have to just pass the payload which can be string cons char or you wind yeah we have like a few overloads of this method what i'm gonna do is to create a separate function to send that error message where i take a cons char error and in there we're going to define hri let's just call it message and let's define another size for it so let's maybe call it message size let's give it 128 and that message size is going to be the size of the of the message char array then with sprint f we're going to prepare first a json message the action so action it's good to have an action field because that's that's the default format of a json that we send to api gateway websockets so if you define a custom route in that then you pass that route as an action in a json message and that's why i'm just gonna go for it otherwise we would have to handle it sort of differently and for that action let's just call it msg ask for the message and then i guess let's just continue with a message which is going to be another object and that object is going to have status error but yeah really here you can you can go with whatever format you like i'm just gonna go with this one so i will have always action and then the body of that action maybe i rename it to body but one second so status is error and the actual error is going to be that one that's passed to the send error message function okay so let's just do that and you know what maybe let's just change this to body okay nice um this is quite okay the other should be fine 128 yeah in case error is longer maybe let's double the size yeah and then we what we want to do is to just send that message back okay so now having this function this helper send error message function i can just send that error c string back to the to the websocket server okay cool so now we can start reading some information from that json that we have passed with this serialized json and and for that let's think about another format of a message that esp32 is going to be um basically supposed to receive from another client so something very similar to the error message but this message instead of like having an error should have some information about the command that needs to be executed on the microcontroller so um and yeah by the way i'm just thinking maybe instead of status we just call this type so we'll have a body and there's gonna be type and and error maybe that is better yeah i will think about it i will think about it because we want to simplify this as much as possible maybe this is not the best one maybe we could have an action msg then type and then body i guess yeah maybe that would be better because i i started thinking about the the other type of message that we want to receive this time that that's the not the type that we're sending and you know what i think it will be better if this is not nested so we have action msg type is error and the body is the actual error here so that's gonna stay flat and the same type of message the same format will be something that we accept uh that we expect to to get in in in here in handle message if the the json is gonna get a passed successfully without the network um but yeah obviously it's gonna be a different type so you know what let's just do another if statement to make sure that there is a type field let's just make it required so on the dock what we're gonna do is to call ease method that checks if that member of a json that we received has a c string type okay so it has to be string on the json basically the json string and and if it's not let's just send some error message like invalid msg type maybe message type and then retap okay so that's something we have checked now the second if let's do if dog type and here we're going to treat it as a c string so i'm going to do string compare and i'm going to compare it with the cmd let's call it cmd ask for the command and if it's identical to commands i mean if it's cmd there is no it's not case sensitive i think if you do str cmp but that's fine if it's a cmv that means we're going to send we're going to call some sort of command on the microcontroller okay and in that case we're going to check for the for the body so we may actually we might actually have another level so then let's just do let's treat the body maybe let's check the body if it's there this is also good to have a validation like that so we're gonna do dog body and again is but this time we want to check if this is a json object and if it's not the json object then let's send the message invalid command body okay right so at this point we know that the body is another nested object and what we can do is to continue checking for what is in there okay and what is in there we need to know what type of of command is this so it'd be nice to have another type in there so let's just do if body and command sorry and then and then maybe type is yeah i keep forgetting this is not just a simple comparison if you do it in c style so what you want to do is to compare this with the first type of command that i'm thinking about handling which is going to be the pin mode okay yeah let me tell you about my plan here we want to keep it simple we only have a diode with a resistor that we're going to connect to esp32 dev kit today so i'm going to handle three different functions pin mode digital write and digital read so we will have a right changing the state of something on the on that is wired up to the microcontroller or it can be the built-in led if you want or reading the state of the pin so there's also gonna be that bit where the message is received by esp32 the information is read is red and then it's sent back okay but we're gonna start from from pin mode so this is the right operation so for pin mode what else we gonna have is the pin this is basically required right because if we call pin mode what we have required is the pin itself so let's do dog body and pin okay we don't need to cast it to end or anything because that's gonna be automatic although it is a good practice to have validation on these fields as well i don't want to add them because that's going to take too much time i will just make sure that when we produce the messages they're going to be in a correct format but again i encourage to have gas against anything especially if it's the data coming from the external source but yeah i'm gonna skip it for now here for the video to not be too long okay so the pin and then the actual mode which can be one of the three as far as i remember this can this can be input input pull up and output let's just handle it by um maybe let's take a string this might be a good idea because i think these are just three numbers or something to make it nicer to what is actually the second parameter called in there in the pin mode i need to remove that let's see so if we do that is mode okay so two 2 mode which is by the way unsigned in eight and we're gonna accept cons char const char pointer and what's gonna happen here is just another comparison really so let's just uh steal some code from here and this is just gonna be not the type doctype but the value that we compare with the let's just use output i don't know what's the default one but let's use output and for output we're just going to return that output constant and then we will have a input pull up and input is going to be the default one since i'm not handling the errors you know not that i'm not that photo because um time is limited unfortunately but yeah there should be some validation here if you want to be exact with this otherwise yeah there is sort of a fallback mechanism right so that's going to be two modes and because this takes cons char we can also just provide document with the mode let's just call it mode so we have pin and mode okay yeah so that is the pin mode obviously i need to not forget about the return and two more so two more let's just copy paste twice and let's just change pin mode to digital write and digital read for the digital write this is a very similar situation however instead of mode let's call it value and basically this has to be digital right and we don't need to do that right basically the second the second argument to digital write is is integer as well but it's really zero or one that is accepted then for digital read this is slightly different situation this is what i was talking about this is where we're gonna have a difference because digital read should return a value right it should send a message back with that value that's been read from the pin which is going to be one or zero but yeah let's then use the approach from send error message but this is a different format of message right so let's just do maybe something like that but with a different type of message which is gonna be let's call it type so instead of error it's not gonna be command right it's more like an output maybe let's call it output and let's just say the body is just gonna be that value but because it's an end it's not going to be wrapped in the double quotes and then we just send it back okay so we'll have action message type output and that's going to be the body from the digital read obviously written at the end okay so we have these three types of operations covered some error handling sort of validation some more validation could be added but i guess i will do it off the camera and include it in the example in the code example when i publish the video and we can add one extra step which is let's just add here an error and let's just call it invalid command type and another one at the very end which is which is the one okay so there is invalid message type invalid message type i think it's the same as as this one really maybe let's call it message type format and here we can actually say unsupported message type unsupported message type here also we can have like a unsupported command type instead of invalid because that's not invalid type it's just something that is not supported by the board maybe it's going to be in the future cool okay so i think this is pretty much it in terms of esp32 code or actually yeah there is still one thing missing in in here and that's something i mentioned before which is the acknowledgement message for pin mode and digital right it would be nice to still send some message back just saying something that the status is okay that the device just you know received the message and done the action that was that was told to do and for that i think i'm thinking about just defining yeah let's maybe maybe let's just define another helper function based on center or message but this time sent okay message let's call it send ok message and this will be simpler let's just let's just send the hard coded c string as a body okay and type status let's just do something like that and that send ok message is going to be called into places which is pin mode just before the return and with digital right so just as an acknowledgement okay and i think now this is it of course we have still two pieces of information missing which is the ws host and wsurl but as soon as we obtain this data i am going to add them okay so yeah now the second step is the websocket server and for that we need to create a serverless project and to create this project let's have a look at the template i've got on my github serverless aws node.js typescript v2 it's a new template for for typescript serverless projects that can be used with a version 3 of aws sdk basically there is no dependency aws sdk dependency here anymore because that dependency was version 2 version 3 is modular so you only install the clients that you need to use like dynamodb or api gateway management api that we're going to use anyway let's just go to usage and let's copy this command and now let's go back to visa studio code and let's open the terminal actually i'm just gonna use my own system terminal because i have my projects web development projects in a project directory and in there i'm just gonna paste that serverless create command but let's call the project websocket server api gw enter that should create a folder for me with this name that i can go to now api gw and i can just run code and dot which should open visual studio code this project in visual studio code okay perfect so yeah the project is created and now and now where we're gonna start from is a serverless.yaml where we define the infrastructure configuration and this configuration i have already prepared so let me just paste it in okay it's not much in there let me start explaining it from the resources so there is one resource defined that i called clients table which is going to contain all of the clients connected to the websocket server there is going to be only one field on on these documents which is a connection id and that's that's the only thing that we really need which is going to be required to be able to send the message from the server to the client it is a bit tricky with aws you need to use a special api api gateway management api and that api requires connection id to send a message right so if you want to send a message let's say server receives a message from web application client to send let's say a command to do a pin mode on esp32 upon receiving that message it needs to get a connection id somehow of the other client and send a message there using api gateway management api and because everything is serverless there's not really any place that keeps the the state of all of the connections doesn't keep it and that's why we need to have a persistent storage dynamodb to store these connections whenever client connects we're gonna just add the new client to this table with the connection id whenever clients client disconnects we're going to remove it and when we send a message from one client to another client then what this server is going to do is to scan for all of these clients for connection ids and it's gonna send a message to all other clients right so all the clients except for the client that send the message right the sender of the message but yeah i'm going to show you once i've write the code this is going to be very simple to do especially comparing to the previous app that i've built the web chat it was way more complicated than what this is gonna be because here yeah this is just one client's table with one field then the role statements for that clients table for put item delete item and scan operations basically for these three functions um this one should be actually msg not send message so one handler really three different events connect disconnect msg with access to to that resource clients table on these three operations and two environment variables we have clients table name which is used in there that's the actual name of the table that's going to be created on aws and we've got wss apa gateway endpoint this is required by api gateway management api to send these messages from the server to the client something i've just i just explained a couple minutes ago okay um there is one more thing that you may have noticed which is this prefix in here self provider stage i introduced a stage field to the provider with a default value as depth so this is simply adding a prefix to the name of the of the table so you can have more environments not just death however we're just gonna work on that okay let's have a look at the handlers file because this is probably just the hello handler that we have to rename too much this name so that should be handler that should be handled and also we don't have our dependencies installed so let's open a new terminal and let's run yarn okay dependence is installed i'm going to add two more but for now let's just start the implementation okay so first of all the body is always gonna be an empty string and what we need to do as two first steps is to extract connection id from the event that is going to be on the request context and we need to make sure this is a string not string or null and the same thing we have to do for route key so this is almost the same like that but route key and there is one more const constval body okay which is essentially event body or an empty string then switch case for the route key that can have one of the three values the values that we defined in the functions which is connect disconnect and msg so let's handle them connect disconnect and this is gonna be msg the custom type okay depending on what route key is we're going to return a result of three different functions handle connect is the first one which is gonna be asynchronous function that takes connection id connection id as a string and it returns a promise of api gateway proxy result then we will have a disconnect and finally msg they all gonna need connection id however for the msg i'm gonna be more explicit let's call it this connection id i'm going to explain when i'll implement when i've been implementing this this this function cool um for handle connect this is the moment where we need to install the dynamodb client so i'm gonna run yarn add aws sdk dynamodb client dynamo db okay dynamodb added to the packages json and installed let's create an instance dynamo of dynamodb client new dynamo okay we're gonna need to import it from dynamodb and that is dynamodb client where we can just pass an empty config because that's all going to be figured out automatically what sort of region is this based on the environment variables uh passed automatically by serverless framework the way that several this framework configures this whole thing okay so with dynamodb client now for handle connect what i am going to do is to call dynamodb client send new put item command where i'm going to pass a table name which is clients table name that we can extract from the environment variable which is this environment variable clients table name or empty clients table is the table name and the item this is where we have a difference comparing with with a sdk version 2 of aws because here we still have to provide the key which has to match the partition key that we have defined in serverless.dml in the resources connection id right but for the value of that key has to be an object with this additional kind of key specifying the type where s is a string type and this is where i can pass the connection id so what this is gonna do is to create a new document in dynamodb table clients with connection id only and the connection id of the client that has just connected to the websocket server and then right after that i can return status code 200 and body empty which is a message okay and i'm going to extract it to a separate to a separate const var in a second once i handle the disconnect for now let's just call handle connect with the connection id okay and now disconnect so for the disconnect this is gonna be very similar to what i have in the handle connect but i think now it's the time to extract it to two response okay const var that i'm just gonna response okay i'm just going to create it there the top okay and now let's just copy paste this fragment to the disconnect and let's change that to delete item command and let's change item to key and this is pretty much it to disconnect you just need to send the delete item command with the key where the the pattern is exactly the same you need to provide partition key and then the type of that of that field which is a string and the actual value that comes from the argument connection id in here right the difference really is if we had more fields on that item on the document on the client we would have to specify all of them in the item right all of the required ones if we had specified null ones we wouldn't have to but for others probably yes otherwise you would have got an error an exception okay so handle disconnect is done i'm going to return handle disconnect also with connection id okay so basically whenever connected client disconnects we want to remove that entry from dynamodb table right that connection id right the final handler which is the handle msg and here apart from the connection id we're gonna need body right this is where we want to pass the message from one client to the others so let's just uh maybe let's just do it fast so handle msg with connection id and body and then what we're gonna do is another operation on dynamodb client this time is a scan command scan command only needs a table name but it returns i mean all of them return output also this command and this command but for the for the scan command it is actually irrelevant because if the output count and output count is greater than zero we want to do something otherwise we are okay with returning response okay i guess we could create a warning message that's also an option and send it back to to the to the sender and i'm just thinking this might be a good idea but yeah let's let's leave it for now because for that i would have to use apa management api gateway management api yeah maybe we will do it but for now let's just handle all the other clients except for the for the senders or the the recipients of that message and what we want to do is to iterate over the items yeah this can be null so let's just make sure there is always something to iterate on even if it's nothing empty are i and then on item this is where we going to use the ap api gateway management api but let's create another function send message so this will this function will be responsible for the actual for sending the message this is just to do the handle to iterate over to do the scan basically and iterate over the items then call send message yeah there is one important thing to be done in here which is another if statement that is going to compare the item which is okay that is the record of string to attribute value so we will have connection id on the item dot s so basically now it's all the way around the way we access the values of the field in dynamodb because that's the basically that's the data structure right so i'm just following this we have item connection id dot s and if they are different then this connection id then the sender connection id right that's the sender connection id and all of the items are potential recipients we want to make sure the recipient is not the sender right we don't want to we don't we don't want for the recipient for the sender to send the message to to themselves right okay and this is where await send message is going to be called with two parameters connection id this is not this connection id anymore that's going to be this that's going to be the recipient connection id right the recipient connection id and body is going to stay the same now this might be okay this might be undefined so let's just do that i mean this this is not really going to be undefined with our current configuration okay and send a message let's install apa gateway management api so yarn add and that's going to be clients api gateway management api dependency added let's create an instance of it so i am going to import it from yeah that's gonna be this and that is gonna be api gateway management api and we need to create a client so let's call it apa gateway management api api gateway management api and this is important because this is where we want to pass the end point which is a in an environment variable right this environment variable wss api gateway endpoint which is essentially the the api gateway that is going to be created but because this is api gateway web sockets the end point we're gonna get has a wss protocol and the one that is needed by api gateway management api is a http https protocol that's why this environment variable has to be created separately based on the ref values of websockets api and region with execute api a subdomain so that's that's that's basically how it works with with api gateway web sockets this is essentially from the from the documentation okay and now we want to call post to connection where there are two fields in the in the config object that we need to pass which is connection id something that we have and the body and the data essentially which is the body but the data has a different data type that is required which is an unsigned int 8 ri it's a bit unusual for javascript but um yeah this is required so what we what we what we can do is to use a built-in text encoder util class encoder text encoder that can encode a string type of a body into into unsigned ins array okay so that is the send message one edge case though that is good to handle is the gone exception the gun exception happens when you try to to post to the connection that doesn't really exist but for some reason it hasn't been removed from dynamodb right so let's say we have a connection id of a client that's just disconnected or for some reason the connection of that client dropped but the disconnect hasn't been called yet or hasn't been called at all and we we're trying to post a message to that connection but it doesn't exist and this is when post to connection method throws an exception and that exception is a gone exception and what we wanna do if we get gone exception is to handle that scenario as a disconnect and that's just it let's wait for it though to make sure that connection id gets deleted and we don't want to treat it as an actual error if it's something else then we can still throw an error that's gonna be basically propagated to the top which is in the handle function and then this is just going to be locked to cloudwatch in the end because we don't really catch it anywhere for the sake of simplicity of course okay so um this is this is how the handlers are gonna look like um there is one more thing i was thinking about for maybe sending back a warning message so i am not 100 sure if this is how this is if this is how to send the warning message back but i think if i just stringify a json here as for the body when i send that back okay that's not how it's gonna work so i still need to await and send a message when this is a different case so if this is not yeah i guess we want to response okay regardless of what happens but here we want to send a message back to this connection id with the information that the the sending of the message failed there was no one to send the message to so let's just maybe follow this format okay from the main.cpp from our esp32 code and let's just pass that instead i mean not instead just pass that as the actual message so that would be a message type we can just call it warning and the body could be no receipt recipient maybe that's just to make it simple okay so that's what we're gonna do let me just quickly go through the code again so a handle a function this is where it all starts right this is this is what's going to be triggered whenever connect disconnect or msg happens and what we do first we extract connection id route key and body depending on the route key we're doing one of this three different we call one of these three different handlers if the client connects to the websocket server handle connect connect is executed and we create a new client in dynamodb with the connection id and that's it if the route key is disconnect client is just disconnecting from the server what we doing is to remove that client from dynamodb table and finally when msg is received as a route key then we handle that by doing a scan command on dynamodb table to get all of the clients from the clients table then making sure that there are clients in the results right the count of the of the items that that are in the output from dynamod from the scan command are greater than zero and if this is the case what happens is we iterate over all of the items and we send that message we pass that message to all of the recipients all of the other clients but essentially there's just going to be one client in case of sending a message from web application to esp32 and then the case for having no recipients so let's say esp32 is switched off and we send the message from the web application then what we're going to get back it's just a warning that the recipient is not there right esp32 is offline but yeah basically this this could be applied to like multiple esp32s you could essentially with this you could control all of them but they will all get the same command right so so this code needs some modification if you would like to control more esp32 independently okay and yeah for the send message itself the piece of code that posts to connection we just handle one edge case for the gone exception when the client is no longer connected but we still try to connect we still try to send a message to that to that client sorry we still try to send it and then we basically disconnect remove that client from dynamodb cool okay so um now let's just deploy this code we can do it by going to terminal and then calling either server as deploy or we can even do something like yarn deploy okay the code deployed and now what we have in the output is the url to newly created api gateway websocket server and what you want to do now is to copy that value and to go back to our esp32 websockets project and then let's paste it to ws host however this part needs to go to ws url and the protocol can be removed because that's been already passed in the begin ssl okay so and now what we can do is to deploy that code to esp32 in order to do that let's make sure esp32 is connected to the computer using usb cable and if it is let's just click on the upload button when you see connecting message on the screen with the dots it's time to press on the boot button on the development board and hold it until you see writing then you can release it okay the code deployed let's connect to the serial monitor now by clicking on this button in a few seconds we should see some serial communication from the board okay we can see connected nws connected that means esp32 managed to connect to our websocket server okay and now what is left to build is the third and final project and that is a react.js application our user interface right a place from where we'll be able to send the commands to esp32 microcontroller to create that project i'm going to use another template that i created beforehand and that is a vit react typescript tailwind css template with that template you don't need to do any configuration to have typescript and tailwind css with an example up just like a create react up really so we're going to completely change it to our control panel right that's what i'm going to be building um to use the template the only thing that needs to be done is uh git clone right so we need to clone this repository i'm just gonna copy this and then i'm gonna open my terminal then change directory to the projects directory and run git clone paste what i've just copied and i'm going to clone it into a specific directory which is esp32 control panel okay and now i'm gonna change directory to this newly created esp32 control panel project and i'm gonna open that project with visual studio code by typing code space and dot right okay so i've got a visual studio code opened with esp32 control panel that's the react js application template what i'm gonna do first is to install all the dependencies so i just open a terminal in visual studio code and i'm gonna run yarn chassis that's it okay done and now we can run yarn dev to start the application this is just an example application but i'm going to show you how it looks like so if you create an app with it that's what you're going gonna see that means it is working okay i'm gonna split that browser window and i'm gonna put it on the side so let's just let's just do this so we will see all of the changes i'm making okay nice before i modify this code i have to install an external dependency to connect to the websocket server to simplify that connection so i've just added a new terminal tab and i'm gonna run yarn add react use websocket which is gonna be a custom hook to connect to the websocket server okay so that dependency installed and now i can go to src app.tsx and in there we want to first remove that example code maybe i leave the main container and then let me remove this react logo okay let's import this custom hook to connect to the websocket server so that's gonna be use websocket from react used websocket and let's call this a function use websocket it requires a full url to the websocket server so i'm gonna go to our esp32 websockets project and i'm gonna just copy this host then type the protocol host and the path because yeah as i said it requires full url then what you can do now because use websocket returns an object we can destructure this object into const vars and we only need three of them from from that use websocket custom hook we need last message that's gonna keep the last message received from the server we need send message this is a function to send a message to the server and the ready state this is the current state of the connection okay i'm gonna use those later for now i'm gonna start building this ui this user interface so let's add a heading esp32 control panel maybe and now we need two elements one element to switch the state of the pin okay something like a toggle where i can switch on or off a digital pin change its state to high or low and other than that i'm thinking about some sort of a drop down where i can pick the pin that i want to control right that's one way of doing it you can also have like multiple toggles for every single pin but this is like less flexible so yeah how this is gonna look like let me just show you i've got this nice website with taylor and css components um that's gonna be the one for toggle about the bigger one there is a separate example large toggle so with that we'll be able to control the pin and if we connect led diode to that pin we'll be able to control that diode so yeah maybe let's copy this example so that's the last one from here and let's create a div container and paste it inside that div container we need to do small modifications though because this is just a pure html css i mean pure html example so we need class name instead of class attribute because we using react and that has to be closed that input okay yeah let's see how it looks like nice okay so here we've got the toggle this is how you switch on switch off led diode if it's connected to digital pin now to pick the pin to select it i'm gonna use another component and that component is a simple select so let's just go for the first example from here and again let's maybe add a div and add that example in there also some modifications need to be done like class name id is not really required in there and i'm gonna change the options in a second but let's have a look how it looks like okay so we've got the select an option uh you know what maybe let's call it uh maybe let's do select a pin make the text bigger and the options are not going to be countries right this is going to be the digital pin yeah let's think about these pins now to render options of the select box correctly so maybe i'm gonna create a const bar output pins and let's let's just pick few random pins 18 19 22 and 23 i think these are actual pins on my esp32 dev kit and this is just a numbered array let's go to the select and let's use a map on this array to display all of these options okay option the value of the option it's always gonna be same as the the pin value i mean not the pin value actually the pin sort of id and what we're gonna display is gpio and the pin much better okay so now you can see you have gpio 18 19 22 23 for the large toggle i don't really want this to be a large toggle label in there maybe let's do off and on for now let's do off because that's switched off that's gonna change dynamically when you switch it on that should change to on but that needs that that needs a behavior in this react component to make it happen which i'm gonna add in a second but what i don't like is that there is no separation between select and toggle so let's add maybe a margin maybe something like that okay nice okay cool yeah um and now let's implement the behavior of this drop down and toggle for now without sending or handling receiving any messages from the websocket server so just pure react behavior for the select and for the toggle there are gonna be two separate states the first state i'm gonna call it a selected pin set selected pin use state and that's gonna be uh output pin the first output pin is gonna be default one as it is right now gpu gpio 18 then the second state to handle the toggle is gonna be pin value right for which i'm gonna use a boolean so this is gonna be false as a default as it is right now okay um let's add attributes to the select the value attribute that is gonna be selected pin and on change which is going to change the selected pin to that's going to be e target value it's gonna set a new value right when uh when we change that when we select another pin but obviously we have to use the parseint i think that is correct not like that that is correct yeah so pass into change the value which is a string to int to number because that's the data type of selected pin now the pin value so for the pin value this is going to be very similar although instead of actual value i'm gonna use the checked attribute which is gonna be our pin value and then once doesn't need to capture any event because what we can what we can do is to just invert right just invert the pin value so if it's true it's going to be false if it's going to be true so that's going to be our new value okay okay nice so now here nothing is really going to change with this behavior but internally we have two new states now and they change whenever we change anything in there and you know what thanks to that i can fix that off on so depending on the pin value i can have on or off displayed in there so when i switch it on we've got on when i switch off we've got off this is this is nice now time to send a message to esp32 microcontroller with send message function from use a websocket hook this is pretty straightforward the only thing we need to do is first to identify where we want to send the message what's going to trigger that action and then this is this is simple right this is going to trigger that action so whenever these changes right whenever a toggle is toggled we want to send a new message and that message has to be a string type but because we use json json strings basically i'm gonna use the json stringify to send a string but still operate on an object so one second let me just add action right if you remember msg this is used to pass the message right for the server to pass the message to another client so action msg type is gonna be cmd right this is a command and body has type again this is going to be digital right pin a pin number that's going to be our selected pin state and value this is a i think it was an int integer and we've got the we've got the boolean so i'm going to change that basically by doing something like that so if it's if it's true we're going to pass one if it's false we're gonna we're gonna pass zero nice okay so that's how you send the digital right message to the to the websocket server and then that's gonna be passed to esp32 microcontroller with that we can essentially control led diode the only issue here though is that the esp32 control panel app doesn't keep the previous state imagine the scenario where we select let's say gpio22 pin we change the state of that pin the voltage on the pin too high right so by toggling this toggle and then we refresh that page we go to gpao 22 again and we see it as off which is not true right because we've just changed the voltage to high right um the reason why this is like that is very simple right we don't use digital read when we select the pin which should happen by the way right so whenever we select the pin that application should read the state of the pin first before allowing user to change a state of that pin and by the way there is one more thing i've just noticed that is missing and that is the pin mode right we don't have any guarantee that a particular pin that we control with this app is a set to output the mode of that pin is set to output but that's very simple to fix we can just initially when we render this whole component with the use effect we can call this function that is just gonna iterate over all of the output pins and just send a message that is gonna be action msg type is going to be cmd body type pin mode right and pin is gonna be a pin finally the mode is gonna be output so yeah that's gonna make sure all of the output pins will be the actual output pins right otherwise if they're not in output mode that toggle wouldn't work so yeah i've just fixed that nice now when we select a different gpio pin what we want to happen is for the digital read to happen okay so whenever this is changed on change function is executed we want to send a message again that's going to be json stringify action msg then type cmd body type digital reader this time okay that's that's how we're gonna read that information about selected gpio pin and there's gonna be just a pin which is uh which is going to be this bit that we can create a variable for let's call it a new pin so that's what's going to be passed to set selected pin but also to send message body okay so that's gonna handle sending the message to esp32 through the server of course okay so we've got the pin mode that's fixed we've got the digital delete now we need to handle receiving the message okay because once that message is sent esp32 microcontroller is going to send back the output let me show you digital read we have the output type of a message and that has to be handled in the react application to handle that we're gonna look at a last message right this is where all the information about the last received message from the websocket server is kept um but in order to handle it right this this is gonna change over time very often i i would i would assume we want to listen to that change every single change of last message whenever new message is received from the server we want to be able to capture it from here from this app and we can do that with use effect again because if you specify a state which is essentially last message internally this is just a react state that changes whenever that state changes that function is going to be executed and in that function first of all we want to make sure that last message is not null and if it is we want to stop from going further right that's just going to stop executing that function which is retail nothing but if it's not null that means we just received some sort of message what this message is is for that function to identify and then act accordingly right so let's just do that um how to read that message um we have a certain guarantee that the type of that message is json string right it is going to be received a string but we know this is a json whatever comes from the server is json so i can stride away a parse that with json parse so on the last message we have a data field and that data contains this whole string this whole message right once the message is passed we need to um yeah this is go it's got the type of any which i don't really like so let's define a type for the message maybe let's call it message body this is essentially message body and we have an action if you remember again we've got a type and we've got the body which we don't know what kind of type it is at that point until we read what is the what is the type uh let me show you so here i'm just gonna sort of cast it as message body and i'm going to use a simple if statement on a passed message so i want to make sure maybe let's make let's make sure this action is msg because if it's not msg we're going to ignore it it's something not recognized okay so again a return then if this is msg and the type is output right if this is an output output is the type of a message that's received back from digital read right when we send the digital id to esp32 what it sends back is the output type of message and then we know that the actual body from the past message body has a type number right because it is a number zero or one and having this piece of information we can simply call set pin value but one thing to make sure about is to also capture it in with use effect so set pin value um i think there is some issue okay yeah the issue is that these should go in there right the top of the of the react component and then set pin value is going to use that body if that body is zero we're going to pass it sorry yeah we're going to pass it as false otherwise that's gonna be true okay that's how we're gonna do it and that's essentially it right so now we don't really need to keep any state anywhere because that state is going to be set thanks to digital read so whenever the pin is selected the digital read is sent to the server then to esp32 microcontroller esp32 microcontroller is going to send back an output message that output is going to be read in here in this use effect and if the type is really output right we're going to parse the body as a number and we're going to set that value to a pin value state right so we will see in there we will see on if that particular pin that we selected has a high voltage right also one additional issue that i've just noticed is with this gpio 18 the default pin the problem is for that is that the state is not going to update when you open this up right so when we just visit that page that's the default gpio and because it is a default value at that point on change hasn't been triggered yet right you actually need to change something so click on the drop down and change the change it to something else right this is when unchanged function here is executed right when you just open it this doesn't happen there is a simple fix for that which is uh by calling send message digital read when we render this component okay but this has to obviously be this this has to be called with the the default pin which is uh the first one but let's let's maybe split it out so let's create a default output pin var which is going to be output pins 0 and then let's go with default output pin as a default here and also in there okay so now we're going to make sure that at the very beginning when this select box has a default pin digital read has happened and that toggle has been updated with the current state of that pin right okay and this is it in terms of controlling digital pins in a real time from this control panel i think we can give it a test now so let's quickly wire up led diode with a current limiting resistor and let's see if we can control that diode here is the circuit diagram as you can see that's pretty straightforward we've got led diode which in my case is rated for 1.8 volts of a forward voltage and 20 milliamps of a current which gives us 75 ohms right at least 75 ohms of a current emitting resistor that i am going to use all of this wired to digital pin gpio 18. but yeah feel free to connect more led diodes to different digital pins because we'll be able to control all of them from our esp32 control panel okay let's just wire it up quickly all connected let's just wait a few seconds for the built-in blue diode to switch on okay so the esp32 is connected to wi-fi now let's now give it a test so um i'm gonna refresh this page and i'm going to keep the gpio18 as a selected pin and i'm going to switch it on okay we can see that led diode is switched on when i switch off it switches off immediately so we have a near a real-time communication right but it really looks like a real time because for me this is like milliseconds of delay i don't even notice it okay so um what i can do now is to test other gpios so if i select a different gpio let's say 22 for example and i switch it on and then connect my led to that pin instead it is instantly switched on so this is working as well on the other gpios the final test of digital read let's keep this switched on and let's refresh this whole page then let's select gpio 22 and that is being switched on automatically because of the digital delete right that was an instant change of this toggle okay so this is all working fine of course this can be used not only to control the voltage we can do many different things but that depends on what kind of project we're thinking about building and now time for the final step which is going to be to deploy our esp32 control panel to aws sf3 so we can control the microcontroller from any place in the world with internet connection in order to do that i need to run a few commands first what we need is the production build of our website of our control panel to create the production build what needs to be done is the yarn build command or npm run build so i'm gonna hit enter now and that should create a dist directory in my project with the production bit right we've got index html and then one cs css file and one javascript file okay then i need to create aws s3 bucket and the command to create that bucket is with the use of aws cli s3 api which is aws s3 api create bucket where you need to specify bucket flag with the name of that bucket a region with the region where you want to create your bucket in and create bucket configuration flag this is again uh just location constraint with the same region that's been specified for the region flag so yeah i'm gonna hit enter now okay so the bucket is created let's upload our production build to this bucket that is aws s3 sync as for synchronize command and as you can see i've got the dist directory with my production build that's gonna get synchronized with s3sp esp32 control panel right so that's gonna send all of the files from this directory to to the control panel bucket okay that's done the third step is to create a bucket policy okay so i've just created a tmp bucket policy json file right bucket policy json file in a tmp folder i'm going to open it with visual studio code and now i'm going to paste a policy that i've prepared beforehand which is essentially allowing for the public access for everyone on the internet to this bucket but it's just read only access okay on that esp32 control panel bucket now to upload this policy i'm gonna use aws cli again and that is a put bucket policy command where again we need to specify the bucket flag and the policy with the path to that a file i've just created and modified so you're gonna hit enter then i need to press the q button because the preview has been opened okay and the final command which is aws s3 website and this is to specify the index document right which is going to be index html gonna press enter and that's it now we should have a website on the internet working that allows us to control esp32 in a real time right from any place in the world so let's just go to that website which is esp 32 control and panel s3 website then the region and amazon aws.com domain so gonna hit enter and as we can see we've got esp32 control panel if i change gpio to 22 it switches like toggle switches to to on automatically because we've switched that pin on on the on the local right on the local app which stayed there because i've got my microcontroller still on and this is it for today however if you are interested in more content about serverless websockets check out this video where i have built fully functioning web chat application step by step if you'd like to support me don't forget to subscribe to my channel and ring the bell thanks for watching and cheers bye
Info
Channel: Tomasz Tarnowski
Views: 22,192
Rating: undefined out of 5
Keywords: connect esp32 to wifi, getting started with esp32, esp32 tutorial, arduino tutorial, esp32 uart tutorial, esp32 tutorial step by step, random nerd tutorials, wifi tutorial, arduino esp32 tutorial, esp32, arduino wifi, esp32 wifi, esp32 websocket, esp32 websocket server, esp32 websocket client, esp32 websocket json, esp32 websocket tutorial, websocket tutorial, aws lambda, serverless, reactjs, tailwind css, typescript, javascript, c++, arduino, iot, how to connect esp32 to wifi
Id: z53MkVFOnIo
Channel Id: undefined
Length: 108min 56sec (6536 seconds)
Published: Fri Sep 16 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.