Posts tagged with "iot"
One of my university professors once said that “Software is the most complex creation of man.”
I think I’m drawn to software development and to technology in general precisely because it’s complex. It’s a field I know I’ll never reach the extents of. It will never run up against boundaries with how creative I can be with technology, and I’ll never run out of new concepts to learn.
So that’s what I love to do - to be involved with learning and teaching technology. That’s why I usually say opportunities to present online learning courses.
In April 2017, presented a course on Microsoft Virtual Academy called Introduction to Azure IoT. I’ll provide a direct link to the content here as soon as it’s available.
The course served to introduce curious viewers to IoT in general as well as to the broad offerings of Azure in the area of IoT, and it also served to introduce viewers to the more in-depth course on the same subject available on the edX platform. I’ll provide a link to that content too when it’s launched.
For now, I’ll make the full slide deck available and give you a bit of an idea of what the content covered.
You can download this PowerPoint deck to get a deeper sense of what was covered as well as to get a reference to the various external links that I used.
Here are the topics…
Hope this helps you ramp up on IoT!
There are two types of gateways in the IoT (Internet of Things) world.
The first is a field gateway. It’s called such because it resides in the “field” - that is it’s on location and not in the cloud. It’s in the factory or on the robot for instance. Microsoft has an open source codebase for field gateways called the Azure IoT Gateway SDK you can start with.
The second is a cloud gateway, and obviously that one is in the cloud. Microsoft has a codebase for one common cloud gateway function - protocol adaptation available at Azure IoT Protocol Gateway.
Both of these entities exist as a point of communication through which you direct your IoT traffic messages for various reasons.
You’ll also hear the term edge to refer to devices and gateways in the field. The edge is the part of an IoT solution that’s touching the actual things. In the internet of cows, it’s the device hanging on the cow’s collar. In an airliner, it’s all the stuff on the plane itself (which I realize is a confusing scenario since technically those devices may also be in a cloud).
Some possible reasons gateways exist are…
you need to filter the data. It may be that qualifying data deserves the trip to the cloud, but the rest just needs to be archived to local mass storage or even completely ignored.
you need to aggregate the data. Your messages may be too granular, and what you really want to send to the cloud is a moving average, a batch of each 1000 messages, a batch of messages every hour, or something else.
you need to react to your data quickly. It doesn’t usually take that long to get to the cloud and back, but then again “long” is relative. If you’re trying to apply the brakes in a vehicle every millisecond counts.
you need to control costs. You can use filtering or aggregation to massage your messages before going to the cloud to reduce your costs, but there may be some other business logic you van apply to the same end.
you have some cross cutting concerns such as message logging, authorization, or security that a gateway can facilitate or enforce.
you need some additional capabilities. Devices that are not IP capable and able to encrypt messages are dependent on a field gateway to get any messages to the cloud. Devices that are able to speak securely to the cloud, but are not for some reason capable to using onen of the standard IoT protocols (HTTP, AMQP) require either a field gateway or a cloud gateway (such as Azure IoT Protocol Gateway).
What kind of hardware might you end up using for a gateway? Well, the possibilities are very broad. It could be anything from a Raspberry Pi to a very expensive, dedicated gateway system.
Also, Azure maintains a big catalog of certified hardware including gateways that might be the most helpful resource.
There’s certainly a lot more about gateways to know, but I’ll leave this here now in case it helps you out.
I worked together with a few fine folks from my team on a very fun hackathon project, and I want to tell you about it.
Here’s our team…
The hackathon was themed on some relatively new products - namely Cognitive Services and the Bot Framework. Additionally, some members of the team were looking for some opportunity to fine tune their Azure Functions skills, so we went looking for an idea that included them all.
I’ve been mulling around the idea of using some of these technologies to implement an escape room, which as you may know are very popular nowadays. If you haven’t played an escape room, perhaps you want to find one nearby and give it a try. An escape room is essentially a physical room that you and a few friends enter and are tasked with exiting in a set amount of time.
Exiting, however, is a puzzle.
Our escape room project is called Cabin Escape and the setting is an airplane cabin.
Players start the game standing in a dark room with a loud, annoying siren and a flashing light. The setting makes it obvious that the plane has just crash landed and the players’ job is to get out.
Players look around in haste, motivated by the siren, and discover a computer terminal. The terminal has some basic information on the screen that introduces itself as CAI (pronounced like kite without the t) - the Central Airplane Intelligence system.
A couple of queries to CAI about her capabilites reveal that the setting is in the future and that she is capable of understanding natural language. And as it turns out, the first task of silencing the alarm is simply a matter of asking CAI to silence the alarm.
What the players don’t know is that the ultimate goal is to discover that the door will not open until the passenger manifest is “validated,” and CAI will not validate the manifest until all passengers are in their assigned seats. The only problem is that passengers don’t know what their assigned seats are.
The task then becomes a matter of finding all of the hidden boarding passes that associate passengers with their seats. Once the last boarding pass is located and the last passenger takes his seat, cameras installed in the seat backs finish reporting to the system that the passenger manifest is validated and the exit door opens.
Architectures of old were almost invariably n-tiered. We software developers are all intimately familiar with that pattern. Times they are a changing! An n-tier monolithic architecture may accomplish your feat, but it won’t scale in a modern cloud very well.
The architecture for cabin escape uses a smattering of platform offerings. What does that mean? It means that we’re not going to ask Azure to give us one or more computers and then install our code on them. Instead, we’re going to compose our application out of a number of higher level services that are each indepedent of one another.
Let’s take a look at an overall architecture diagram.
In Azure, we’re using stateless and serverless Azure Functions for business logic. This is quite a paradigm shift from classic web services are often implemented as an API.
API’s map to nodes (servers) and whether you see it or not, when your application is running, you are effectively renting that node.
Functions, on the other hand do not conceptually map to servers. Obviously, they are still running on servers, but you don’t have to be in the business of declaring how Functions’ nodes scale up and down. They handle that implicitly. You also don’t pay for nodes when your function is not actually executing.
The difficult part in my opinion is the conceptual change where with a serverless architecture, your business logic is split out into multiple functions. At first it’s jarring. Eventually, though you start to understand why it’s advantagous.
If any given chunk of business logic ends up being triggered a lot for some reason and some other chunk doesn’t, then dividing those chunks of logic in different functions allows one to scale while the other doesn’t.
It also starts to feel good from an encapsulation stand point.
Besides Functions, our diagram contains a DocumentDB database for state, a bot using the Bot Framework, LUIS for natural language recognition, and some IoT devices installed in the plane - some of which use cameras.
The camera module is developed with Microsoft Cognitive Services, Azure functions, Node.js, and Typescript. In the module, it performs face training, face detection, identification, and as well as notification to Azure function service. The module determines if the right person is seated or not, then the notification will send back to Azure function service and then the controller decides the further action.
The following digrams describes the interaction between the Azure fuctions services, Microsoft cognitive services, Node server prcessiong and client.
We use Azure Functions to update and retrieve the state of the game. Our Azure Functions are stateless, however we keep the state of every game stored in DocumentDB. In our design, every Cabin Escape room has its own document in the state collection. For the purpose of this project, we have one escape room that has id ‘officialGameState’.
We started by creating a ‘GameState’ DocumentDB database, and then creating a ‘state’ collection. This is pretty straight forward to do in the Azure portal. Keep in mind you’ll need a DocumentDB account created before you create the database and collection.
After setting up the database, we wrote our Azure Functions. We have five functions used to update the game state, communicate with the interactive console (Central Airplane Intelligence - Cai for short), and control the various systems in the plane.Azure Functions can be triggered in various ways, ranging from timed triggers to blob triggers. Our trigger based functions were either HTTP or timer based. Along with triggers, Azure Function can accept various inputs and outputs configured as bindings. Below are the functions in our cabinescape function application.
- GamePulse: * Retrieves the state of the plane alarm, exit door, smoke, overhead bins and sends commands to a raspberry piece * Triggerd by a timer * Inputs from DocumentDB - Environment: * Updates the state of oxygen and pressure * Triggered by a timer * Inputs from DocumentDB * Outputs to DocumentDB - SeatPlayer: * Checks to see if every player is in their seat * Triggerd by HTTP request * Inputs from DocumentDB * Outputs to DocumentDB and HTTP response - StartGame: * Initializes the state of a new game * Triggered by HTTP request * Outputs to DocumentDB and HTTP response - State: * Update the state of the game * Triggered by HTTP request * Inputs from DocumentDB * Ouputs to DocumentDB
A limitation we encountered with timer based triggers is the inability to turn them on or off at will. Our timer based functions are on by default, and are triggerd based on an interval (defined with a cron expression).
In reality, a game is not being played 24/7. Ideally, we want the timer based functions triggered on when a game starts, and continue on an time interval until the game end condition is met.
An escape room is really just a ton of digital flags all over the room that either inquire or assert the binary value of some feature.
- Is the lavatory door open (inquire)
- Turn the smoke machine on (assert)
- Is the HVAC switch in the cockpit on? (inquire)
- Turn the HVAC on (assert)
It’s quite simply a set of inputs and outputs, and their coordination is a logic problem.
All of these logic bits, however, have to exist in real life - what I like to call meat space, and that’s the job of the controller. It’s one thing to change a digital flag saying that the door should be open, but it’s quite another to actually open a door.
The contoller in our solution is a Raspberry Pi 3 with a Node.js that does two things: 1) it reads and writes theh logical values of the GPIO pins and 2) it dynamically creates an API for all of those flags so they can be manipulated from our solution in the cloud.
To scope this project to a 3-day hackathon, the various outputs are going to be represented with LEDs instead of real motors and stuff. It’s meat space, but just barely. It does give everyone a visual on what’s going on in the fictional airplane.
This is a unique “Escape the Room” concept in that it requires a mixture of physical clues in the real world and virtual interaction with a bot. For example, when the team first enters the room, the plane has just “crashed” so there is an alarm beeping. This is pretty annoying, so people are highly motivated to figure out how to turn it off quickly. A lone console is at the front of the airplane, and the players can interact with it.
One of the biggest issues with bots is discoverability: how to figure out what the bot can do. Therefore, good bot design is to greet the user with some examples of what the bot can accomplish. In our case, the bot is able to respond to many different types of questions, which are mapped to our LUIS intents:
- What is the plane’s current status overall?
- How do I fix the HVAC system?
- What is the flight attendant code?
- How do I get out of here?
- How do I unlock the cockpit door?
- How do I open the overhead bins?
- How do we clear all this smoke?
- What is the oxygen level?
- How do I disable the alarm?
The bot (named “CAI” for Central Airplane Intelligence) was implemented in C# using LUIS. The code repository can be found at https://github.com/cabinescape/EscapeTheAirplaneBot.
My role at Microsoft is that of an evangelist, and that implies that we speak broadly to audiences about technology. Recently, however, my team is working one-on-one with organizations on exciting new Microsoft technologies - making sure they’re exactly what developers need.
One of the projects I’ve been working on recently is called Regatta championed by Baiyin Zhou (@baiyinvc) from Boston.
This entire idea is super fun. Imagine exercising on a rowing machine in a studio in a group session. Now imagine a visual on the wall depicting your rowing machine and those of the other session participants as real boats. As you heave and ho, you see your little boat moving across the screen.
There are just so many awesome scenarios that are possible at this point.
The visual of the boats alone is encouragement for you to work harder to compete against others in the class. But that’s pretty obvious isn’t it. What about the less obvious scenarios? What about being able to add in a phantom boat that represents the group’s average from yesterday’s workout? Now you’re not competing against your fellow rowers - you’re competing with them. You have to beat that phantom boat!
As soon as things in the real world - like our efforts to stay in shape - are digitized and turned into data, we get to do things like capture our progress, integrate with other fitness tracker systems, or even do data analysis to determine some deep learning from it.
This is the type of concept where digital systems really have an opportunity to affect our lives for the better. I keep saying that technology is not so interesting unless it improves our lives. For being such a technophile, I’m actually quite the skeptic, because all too often technology just gets in the way.
The Regatta Project envisions a future with a whole lot of rowing machines deployed with smart devices (the Raspberry Pi Zero at this point) attached to them, and assuring good communication with all of these devices is a great job for Azure IoT Hub.
Go check out the details of the [Regatta](https://microsoft.github.io/techcasestudies/iot/2016/12/10/regatta.html) project and let me know what you think with a comment below.
I’ve been doing a lot of hacking on the Raspberry Pi, and I’ve written a few articles on the topic. I’ve assembled all of my posts here for easy access.
- Accidental Old Version of Node on the Raspberry Pi - I beat my head against a wall for a long time wondering why I wasn’t able to do basic GPIO on a Raspberry Pi using Node. Even after a fresh image and install, I was getting cryptic node error messages when I ran my basic blinky app.Lucky for me (and perhaps you) I got to the bottom of it and am going to document it here for posterity.
- The Most Basic Way to Access GPIO on a Raspberry Pi - I’m always looking for the lowest level understanding, because I hate not knowing how things work. On a Raspberry Pi, I’m able to write a Node app that changes GPIO, but how does that work? Turns out it’s pretty interesting. I’ll show you.
- Easy and Offline Connection to your Raspberry Pi - Getting a Raspberry Pi online is really easy if you have an HDMI monitor, keyboard, and mouse, but what about if you want to get connected to your Pi while you’re, say, flying on a plane?
- Wifi on the Command Line on a Raspberry Pi - How to configure wireless connections on your Raspberry Pi from the command line.
Getting a Raspberry Pi online is really easy if you have an HDMI monitor, keyboard, and mouse.
Subsequently getting an SSH connection to your pi is easy if you have a home router with internet access that you’re both (your PC and your pi) connected to.
But let’s say you’re on an airplane and you pull your Raspberry Pi out of its box and you want to get set up. We call that provisioning. How would you do that?
I’ll propose my method.
First, you need to plug your pi into your PC using an ethernet cable. If you’re a technologist of old like I am, you may be rummaging through your stash for a crossover cable at this point. It turns out that’s not necessary though. I was pretty interested to discover that modern networking hardware has auto-detection that is able to determine that you have a network adapter plugged directly into another network adapter and crosses it over for you. This means I only have to carry one ethernet cable in my go bag. Nice.
If you put a new OS image on your pi and boot it up, it already detects and supports the ethernet NIC, so it should get connected and get an IP automatically.
Here comes the seemingly difficult part. How do you determine what the IP address of your pi is if you don’t have a screen?
The great thing is that the pi will tell you if you know how to listen.
The means by which you listen is called mDNS. mDNS (Multicast DNS) resolves host names to IP addresses within small networks that do not have a local name server. You may also hear mDNS called zero configuration and Apple implemented it and felt compelled (as they tend to) to rename it - they call it Bonjour.
This service is included by default on the Raspberry Pi’s base build of Raspbian, and what it means is that out of the box, the pi is broadcasting its IP address.
To access it, however, you also need mDNS installed on your system. The easiest way I am aware of to do this is to download and install Apple’s Bonjour Print Services for Windows. I’m not certain, but I believe if you have a Mac this service is already there.
Once you have mDNS capability, you simply…
The name raspberrypi is there because that’s the default hostname of a Raspberry Pi. I like to change the hostname of my devices so I can distinguish one from another, but out of the box, your pi will be called raspberrypi. The
.local is there because that’s the way mDNS works. And finally, the -4 is an argument that specifically requests the IPv4 address.
If everything works as expected you’ll see something like…
Again, my pi has been renamed to
cfpi1, but yours should be called
raspberrypi if it’s new.
My system uses 192.168.1.X addresses for my wireless adapter and 169.254.X.X for my ethernet adapter.
So that’s the information I needed. I can now SSH to the device using…
I could just use
ssh email@example.com to remote to it, but I’ve found that continuing to force this local name resolution comes with a little time cost, so it’s sometimes significantly faster to hit the IP address directly. I only use the mDNS to discover the IP and then I use the IP after that.
Provisioning a Raspberry Pi usually includes a number of system configuration steps too. You need to connect it to wireless, set the locale and keyboard language, and maybe turn on services like the camera. If you’re used to doing this through the Raspbian Configuration in XWindows, fear not. You can also do this from the command line using…
Most everything you need is in there.
You may also be wanting to tell your pi about your wifi router so it’s able to connect to via wireless the next time you boot up. For that, check out my post at codefoster.com/pi-wifi. Actually, if you’re playing a lot with the Raspberry Pi, you might want to visit codefoster.com/pi and see all of the posts I’ve written on the device.
I’ve been hacking on the Raspberry Pi of late and wanted to share out some of the more interesting learnings.
I think people that love technology love understanding how things work. When I was a kid I took apart the family phone because I was compelled to see what was inside that made it tick. My brother didn’t care. If it made phone calls, he was fine with it. I had to understand.
Likewise, I knew that I could use a Node library and change the GPIO pin levels on my Raspberry Pi, but I wanted to understand how that worked.
In case you’re not familiar, GPIO stands for General Purpose Input/Output and is the feature of modern IoT boards that allows us to controls things like lights and read data from sensors. It’s a bank of pins that you can raise high (usually to something like 3.3V) or low (0V) to cause some electronic behavior to occur.
On an Intel Edison (another awesome IoT board), the platform developers decided to provide a C library with mappings to Node and Python. On the default Edison image, they provided a global node module that a developer could include in his project to access pins. The module, by the way, is called libmraa.
On a Raspberry Pi, it works differently. Instead of a code library, a Pi running Raspbian uses the Linux file system.
When you’re sitting at the terminal of your pi (either hooked up to a monitor and keyboard or ssh’ed in), try…
You’ll be taken to the base of the file system that they chose to give us for accessing GPIO.
The first thing to note is that this area is restricted to the root user. Bummer? Not quite. There’s a way around it.
The system has a function called exporting and unexporting. Yes, I know that unexport is not a real word, but alas I’m not the one that made this stuff up, and besides, who said Linux commands had to make sense?
To access a pin, you have to first export that pin. To later disallow access to that pin, you unexport it.
I had a hard time finding good documentation on this, but then I stumbled upon this znix.com page that describes it quite well. By the way, this page references “the kernel documentation,” but when I hit that link here’s what I get…
Now keep in mind that to follow these instructions you have to be root. You cannot simply sudo these commands. There is an alternative called gpio-admin that I’ll talk about in a second. If you want to just become root to do it this way, you do…
If you get an error when you do that, you may need to first set a password for root using
To export then, you do this…
And the pin number is the pin name - not the header number. So pin GPIO4 is on pin 7 on an RP2, and to export this you use the number 4.
When you do that, a virtual directory is created inside of
gpio4, and that directory contains virtual files such as
active_low. These files don’t act like normal files, by the way. When you change the text inside one of these files, it actually does something - like perhaps the voltage level on a GPIO pin changes. Likewise, if a hardware sensor causes the voltage level on a pin to change, the content of one of these virtual files is going to change. So this becomes the means by which we communicate in both directions with our GPIO pins.
The easiest way, then, to read the value of the
/sys/class/gpio/gpio4/value file is…
To write to the same file, you have to first make sure that it’s an
out pin. That is, you have to make sure the pin is configured as an output pin. To do that, you change the virtual
directionfile. Like this…
That’s a fancy (and quick) way to edit the contents of the file to have a new value of “out”. You could just use
nanoto edit the file, but using
echo and the direction operator (
>) is quicker.
Once you have configured your pin as an output, you can change the value using…
Now that I’ve described the setting of the direction and the value, you should know that there’s a shortcut for doing both of those in one motion…
There’s more you can do including edge control and logic inversion, but I’m going to keep this post simple and let you read about that on the znix.com page.
Now, although it’s fun and satisfying to understand how this is implemented, and it might be fun to manipulate the pins using this method, you’ll most likely want to use a language library to control your pins in your app. Just know that the Python and Node libraries that change value are actually just wrappers around these file system calls.
When you call the
write() method in this library, it’s just calling the file system.
So there you have it. I hope you feel a little smarter.
I beat my head against a wall for a long time wondering why I wasn’t able to do basic GPIO on a Raspberry Pi using Node. Even after a fresh image and install, I was getting cryptic node error messages when I ran my basic blinky app.
Lucky for me (and perhaps you) I got to the bottom of it and am going to document it here for posterity. Let’s go.
The head beating happened at a hackathon I recently attended with some colleagues.
The task was simple - turn on an LED. It’s so simple that it’s become the “hello world” app of the IoT world. There’s zero reason in the world why this task should take more than 10 minutes. And yet I was stumped.
After a fresh image of Raspbian, an install of NVM, and then a subsequent installation of Node.js 6.2.2, I wrote a blink app using a variety of modules. I used
onoff, and finally
johnny-five and the
None of these strategies were successful. Ugh. Node worked fine, but any of the libraries that accessed the GPIO were failing.
I was getting an obscure error about an
undefined symbol: node_module_register. No amount of searching was bringing me any help until I found this GitHub issue where nodesocket (thanks, nodesocket!) mentioned that he had the same issue and it was caused by an NVM install of Node and an accidental, residual version of node still living in /usr/local/bin. In fact, that was exactly what was happening for me. It was a subtle issue. Running node -v returned my v6.2.2. Running which node returned my NVM version. But somewhere in the build process of the GPIO modules, the old version (v0.10) of node from the /usr/local/bin folder was being used.
There are two resolutions to this problem. You can kill the old version of node by deleting the linked file using
sudo rm /usr/local/bin/node and then create a new one pointing to your NVM node. I decided, however, to deactive NVM…
…and then follow these instructions (from here) to install a single version node…
I like using NVM on my dev machine, but it’s logical and simpler to use a single, static version of Node on the pi itself.
EDIT (2016-12-14): Since writing this, I discovered the awesomeness of nvs. Check it out for yourself.
And that did it. I had blinky working in under 3 minutes and considering I get quite obsessive about unresolved issues like this, I had a massive weight lifted.
BTW, through this process I also learned about how the GPIO works at the lowest level on the pi, and I blogged about that at codefoster.com/pi-basicgpio.
I hate hooking a monitor up to my Raspberry Pi. It feels wrong. It feels like I should be able to do everything from the command line, and the fact is I can.
If you’re pulling your Raspberry Pi out of the box and are interested in bootstrapping without a monitor, check out my other post on Easy and Offline Connection to your Raspberry Pi.
Afterward, you may want to set up your wifi access - that is, you want to tell your pi about the wireless access points at your home, your coffee shop, or whatever.
Doing that from the command line is pretty easy, so this will be short.
You’re going to be using a utility on Raspbian called
wpa_cli. This handles wireless configuration and writes its configuration into
/etc/wpa_supplicant/wpa_supplicant.conf. You could even just edit that file directly, but now we’re talking crazy talk. Actually, I do that sometimes, but whatever.
…to see what the current status is. If you get
Failed to connect to non-global ctrl_ifname: (null) error: No such file or directory, that’s just a ridiculously cryptic error message that means you don’t have a wifi dongle. Why they couldn’t just say “you don’t have a wifi dongle” I don’t know, but whatever.
If you do have a wifi dongle, you’ll instead see something like…
Yay! You have a wireless adapter, which means you likely have a wifi dongle plugged into a USB port. It says here that the current state is
INACTIVE. That’s because you’re not connected to any access points.
To do so, you need to run scan, but at this point, you may want to enter the wpa_cli interactive mode. That means that you don’t have to keep prefixing your commands with wpa_cli, but can instead just type the commands. To enter interactive mode, just do…
To get out at any time just type
Now do a scan using…
It’s funny, because it appears that nothing happened, but it did. Use…
…to see what it found.
This scanning step is not necessary, by the way, there’s a good chance you already know the name (SSID) of your access point, and in that case you don’t need to do this.
Next you create a new network using…
You’ll get an integer in return. If it’s your first network, you’ll get a 0. That’s the ID of the new network you just created, and you’ll use it on these subsequent commands.
To configure your network do this…
Something I read online said that as soon as you enter this, it would start connecting, but I had to also do this to get it to connect…
Now there’s one more thing. If you’re like me, you don’t just connect to a single AP. I connect from home, my mifi, my local coffee shop, from work, etc. I want my pi to be able to connect from any and all of those networks.
Adding more networks is as easy as following the instructions above multiple times, but you want to set one more network property - the priority. The priority property takes an integer value and higher numbers are higher priority. That means that if I have network1 (priority 1) and network2 (priority 2), and when my pi boots it sees both of those networks, it’s going to choose to connect to network2 first because it has the higher priority.
Okay, that does it.
If you want to see everything I’ve written about the Raspberry Pi, check out codefoster.com/pi
I’m going to keep much of my latest project under wraps for now, but I need your help designing one piece of it.
I’m looking to create a device that is small and light and will dispense one piece of candy at a time programmatically.
Here are the requirements…
- Light - no more than 1oz
- One at a time - it needs to dispense a piece of candy one at a time. If I have to constrain it to hold only round candy of a set size, then so be it, but ideally it would hold candy of varying size and shape. Ideally it will hold Runts bananas.
- It has to operate on a 3.7V power supply
- It has to be an electro-mechanical assembly that I can control with an IoT device (an easy req to fulfill I think)
- It should hold some reasonable number of candy pieces - say at least 10.
So far I’ve thought of the following…
- An Archimedes screw that “pumps” candy up from the bottom of a hopper (like this)
- A wheel that sits horizontal and on each incremental rotation lets one candy piece at a time fall into a hole in the top and lets another fall out a hole at the bottom
- A screw that sits on a horizontal axis that is loaded with candy pieces (one per screw rotation… not in a hopper) and pushes one piece out at a time when rotated 360 degrees. A bit like snack vending machines.
If you can come up with any other brilliant ideas, I’d be glad to hear them.
Thanks in advance.