Join Michael Lehman for an in-depth discussion in this video Implement state machine, part of IoT Development with Windows 10 IoT Core 2: Enabling the Cloud and Security.
- [Instructor] Now it's time for us to implement our state machine. You've already seen the first timer, which checks the joystick. And now we're going to modify that to call a state-specific method. 'Member, our states look like this. Start, LightShow 1, Get Forecast, and so forth. So each time the timer ticks, it's going to look at the current state and call a method that's particular to that. So what will happen then is that each state is then responsible for changing the global state variable, so that you move onto the next state once that particular state is complete.
Timer2, which we're going to add right now, takes every five seconds, and simply acquires the the updated temperature from the temperature sensor and sets it into a global variable, and writes it to the log. So, again, let's review our states. We've got eight states here, the starting state, LightShow 1, Get Forecast, Ask User Temperature, Ask User Cast, Light Show 2, and then either Monitor Day or Monitor Night. So we'll begin by implementing the second timer and then start creating our state variables.
So now let's head off the visual studio. All we basically needs to do is duplicate the timer things, and that is second timer tick method, to go get that stuff. So here we are, here's timer. We need a Timer2. And since we only call it every five seconds, since getting the temperature takes a lot less time than the cycle time of the timer, we don't need an inTimerTick2, because there's not a possibility that we're going to get recalled by the timer while we're in the tick method getting the temperature. Now we'll come down here and create a timer2, just like this.
Timer2, timer2.Interval is 5,000 milliseconds or five seconds. And timer2.Tick is going to be Timer_Tick2, which we now need to define. All right, here we go. So private async void Timer2, Tick2. All right, first, we go see if our senseHat variable is null, means we may have been called during initialization, in which case we're simply going to return.
Now that we know that we have a senseHat, let's go update the temperature, senseHat.Sensors.HumiditySensor.Update. And to get HumiditySensor, we have another using we have to add. We have to add a project reference to the IMU library. There we go. Now we've updated the sensor, let's go see if we have a new value. You can do that by saying if (senseHat.Sensors.Temperature.HasValue), then we know we've got one.
So we'll go get it, double_temp = senseHat.Sensors.Temperature.Value. And then we'll turn it to a string. So now we're going to write it to the log, but we're going to write it to the log as a nice two-character temperature. So we're going to do this this way. We have it as a string, now we need it as an int, int temperature is int of Math.Round, and then we write it to the log.
All right, there we go. So, first of all, we should see this in action. And then we'll go from there. Ah, remember what we did the last time? Right, we forgot to start the timer. And we just did that here as well. So we need timer2.Start. All right, and this is set up for Timer_Tick2. We're all good, let's give it a whirl. Look at that. Revealing new temperatures. I must not have put 5,000 in the temperature, 'cause it's calling it fairly rapidly.
Let's go back and check. Yes, I did. I put 500 milliseconds, instead of 5,000 milliseconds. So we were checking it every half a second. Let's give it another whirl. There, now we're going to have to wait for five seconds for this first tick. There we go. And now our next temperature will come five seconds later. There we go. Now these temperatures, as you can see here, are in celsius. It's not 33 degrees here in the recording booth. I'm not freezing to death.
So let's go ahead and convert that to Fahrenheit as well. And to do that, we'll go to our Timer_Tick. And after we get the temperature as a double, we'll fix that. And we'll say temp = temp times 9/5 + 32. Now let's give that another whirl. There we go.
Now, again, it's really not 93 here in the booth, or 92. But that's one of the things that is going to be left as an exercise to the reader, partly because Microsoft hasn't completely implemented this yet. Typically, the values are about 20 degrees warm. But, eventually, Microsoft will implement the ability to get the CPU temperature, which is what is actually causing this variation. So you can basically just subtract 20 right now to get a more reasonable value. Or when Microsoft implements the ability to get the CPU temperature, you can go back and compensate the temperature for the CPU temperature in a dynamic fashion.
So right now, we're just going to subtract 20, and then get on to building our state machine. All right, let's fix that. Take the temperature we've calculted, temp -= 20. And one more quick go. See if this looks like a reasonable temperature here in the booth. And there we go. It's a nice balmy 72 inside the air-conditioned booth here, in Carpinteria, California. All right, let's get on to our state machine. Back to Visual Studio, back to the top.
And let's go right here. We need to create a new enum to map the states that we're going to have. So, in this case, private enum States. And our states are STARTUP, LIGHT_SHOW_1, GET_FORECAST, ASK_TEMP, and ASK_FORECAST. 'Cause remember we're going to ask our user, "Do you agree with the forecasted temperature? "Do you agree with the forecasted forecast?" Then LIGHT_SHOW_2.
We're basically going to be doing a timer-driven random setting of the color pixels, so that it just looks like the device is actually doing something. And that's a very important thing. As you're building an IoT device, it's important to consider, how do you indicate to your user that it's actually working? Now if it's a completely headless device attached to a shipping container, that's one thing. But if it's a consumer device in the home, or in the enterprise, it's really good to provide some kind of visual feedback just to let people know things are moving. If you know that Amazon Alexa, for example, there's this colored blue-green lights that swirl around on the top, it lets you know it's listening.
So that's what we're doing with these light shows. And then, finally, we've got MONITORING_DAYTIME, MONITORING_EVENING, and WEATHER_ERROR. In case we can't get the forecast, obviously we can't go and ask the user what's going on. Now, this is great, we've got that. But sometimes we'd like to be able to print this out. So in order to be able to print these out, we're going to go ahead and create an array of strings that will store the names of the states so that we can use that to log things.
All right, well now just in the interest of time, I'm going to paste that in. So there's our array of state names that match exactly what we have for our states there so we can print it out. All right, if you're familiar with building state machines, you know you need to have a current state. So let's create one of those. And we'll set it to STARTUP. Then we need to know whether the state has changed, because the logic that's going to work here is that the timer keeps ticking. And each time it ticks, it calls the method that's appropriate to the state that we're in.
But let's say that you've done the light show, we've called Light Show number one theme, say, 10 or 20 times. And the lights have flickered randomly. Now we want to move on to the Get Forecast state. Well, how do we do that? We do that by changing the current state variable, and then having another variable called state change that indicates that we need to go ahead and change that. And we use that also to do the logging. So let's do this, private Boolean stateChanged = true, 'cause in our initial state, the state has changed.
All right, now let's go ahead and implement our state mechanism. So what we're going to do is we're going to come back down here to Timer_Tick. And after we've gotten the state of the keyboard and handled the buttons, now we need to say, well, what's going on here? What cycle are we in? So we do switch(currentState). What did we call it? We called it currentState, there we go. Back to the Timer.
That looks better, switch on currentState, and now we have a case for each one, case States.STARTUP. And there we say we call Startup and break. All right, so in the interest of time, I'm going to go ahead and paste the rest of these in. And you can see for each of our states, in that state we call an appropriate method. So each time we come into the timer tick, we call that method. Now one of the things that's useful is to know which timer tick you're talking about.
So a very useful things to do, which we're going to do here, is to create a variable called cycleCount, initialize it to zero. And then each time we come into our timer tick, right before we switch on the state, we'll update the cycle count. Did I misspell cycleCount up here? Ah, forgot to say it's an int. There we go.
Back to Timer_Tick, back to the bottom here. All right, so each time we come through, we update cycleCount. And the reason we do that, for example for the light show, we can find out, oh, okay, we've come into the light show. And this is our first time, so we save the current cycle count. Each time we come in, we do an update, change the screens, for example, in the light shows, and then compare the cycle count that we started with to the one we're at now. So that after a certain number of cycles, we can move on to the next state automatically.
So let's go ahead and implement blank methods for each of these states. And then we'll proceed ahead to fill them in one at a time. All right, now let's just paste in blank methods for each of our states. Take a quick look. We have StartupState, LightShow1State, LightShow2State, and so forth. So let's begin to fill in the StartupState. In the StartupState, what we really want to do is move onto the first light show. So we set currentState equals to States.LIGHT_SHOW_1.
Then we set the stateChanged variable equal to true. And the reason we do that is when we get to LIGHT_SHOW_1, we need to know whether this is the first time we've come in here or not. And by looking at the stateChanged variable, we can tell. We can say, oh, the state just changed, here we are, do some initialization, set it back to not changed and do all the stuff we do in LIGHT_SHOW_1. Then, when we move onto the next state, we do the same thing, we set the state, we set the stateChanged to equal to true, so that the next state can tell when it's being called for the first time, if that's important to it. Now, the last thing, remember we created that string array because we wanted to be able to log our state changes? So let's create a method called logStateChange, which you'll see me print out the state change to the log, so we can tell what state we're in while we're debugging.
And we'll do that as private void logStateChange. And all it does is get the state from the state names array, and we convert the current state based on the enum. And then we'll write it to the log. All right, now if we run this right now, what should happen is we'll just simply see a new state from LIGHT_SHOW_1, because LIGHT_SHOW_1 is going to get called by the ticker after the startup state runs.
So let's go ahead and give this whirl. And there we are. We're in LIGHT_SHOW_1. And now I've got our temperature showing up. So we've got our state mechanism in place. Let's start filling in the states and making it do something.
- Reviewing hardware options
- Exploring the Sense HAT
- Assembling a prototype
- Designing and implementing UX
- Implementing sensor access
- Reviewing cloud options
- Installing cloud tools
- Sending data to the cloud
- Reviewing Windows 10 IoT security
- Planning for production security
- Moving from prototype to production