Introduction: Brew|LOGIC - Bluetooth Enabled Arduino Brewing Controller

About: I am a hobbyist interested in electronics

Brewing beer has become a worldwide phenomenon. Beer and wine is being crafted in ordinary kitchens in eve­­ry corner of the world. One phrase that you may hear from your fellow homebrewer is "From Grain to Glass", which refers to the process of making beer from raw ingredients.

There are several different ways of making beer. The more advanced way of creating beer, called "all grain" brewing, refers to the "From Grain to Glass". Malted grain is steeped (or "mashed" as it is referred to commonly) with hot water inside a vessel such as a cooler or pot. During this "steep" (or "mash"), enzymes in the grain convert the starches in the grain of choice into sugars, which subsequently dissolve into the water. After the conversion process is complete, the sweet water (or "wort" as it is officially referred to) is drawn off and boiled with hops prior to being fermented with different strains of yeast.

After brewing beer using a 40qt cooler and a turkey fryer stock pot on a propane burner stand, I found myself longing for a more sophisticated and efficient way to brew beer. I kept asking myself: How could I brew beer in the warmth of my kitchen during cold winter months or on late evening brews after a long day at the office?

My research led me down an endless rabbit hole, of which I have yet to find an end to. The result of several years of research has led me to write this article on Instructables. The outcome of my research was a custom built electric brewing system that resembles a sous vide cooker. If you find yourself interested in building a brewing system or sous vide cooker, you found yourself in the right spot.

I have broken up this Instructable into three parts:

  1. The Muscle - the brewing system itself;
  2. The Hearth - a Bluetooth enabled Arduino Uno controller; and
  3. The Brain - a custom Universal Windows Platform App (UWP), created in C#.net, which runs on a Lumia 635 Windows 10 smartphone (or any other device).

The above sections will illustrate how I created a stainless steel behemoth of a brewing pot using a single vessel brewing philosophy (also known as "brew-in-a-bag"). The brewing system is powered by an Arduino control board that regulates the temperature of whatever might be in the pot to a degree using internal temperature probes and a solid state relay controlled electric heating element. I will outline how I progressed from an initial concept idea to a fully functioning layout on a breadboard to soldering a custom circuit board on a piece of perf board (a PCB with copper lined holes that can be soldered).

The final part of the brewing system and the future basis for endless development is my custom app called BrewLogic, which can be run on any windows 10 machine. This app speaks to the Arduino controller via Bluetooth and can be run on any Windows 10 device equipped with Bluetooth. Although Windows 10 is readily available, I have also created a generic Windows Presentation Foundation (WPF) application.

Step 1: Brewing System Type

Before I get too much into the process of assembly of my brewing system, I’ll touch briefly on the why I chose this particular layout. When looking at brewing systems, you are generally faced with a decision as to how you want to brew beer. The main systems that will spark lengthy debates are

  • 3 vessel HERMS (Heat Exchange Recirculating Mash System)
  • 3 vessel RIMS (Recirculating Infusion Mash System); and
  • 1 vessel BIAB (Brew-in-a-Bag)

In principle, all systems produce equally amazing beer. The only reason one might choose HERMS over RIMS over BIAB is personal preference.

HERMS and RIMS use pumps and heating elements to maintain a constant temperature in the mash. Liquid wort is recirculated and heated to maintain the temperature. In a HERMS system, the wort is pumped inside a copper or stainless steel coil that is indirectly heated by a water bath. In a RIMS system, the wort is pumped across a heating element to control the temperature. Both systems have Pros and Cons that are worth considering when choosing what system is best for you. A good starting point for some more research can be found at the below link

https://beerandbrewing.com/rims-herms/

Compared to HERMS and RIMS, BIAB uses a different setup. A single vessel is used to do the entire brewing process from mashing to boiling. During the mash, the grain is held in a strainer bag or stainless steel basket. The water is heated inside the pot using an electric heating element. Recirculation can be provided with a pump but is not required. After the mash is complete, the grain can be lifted out of the pot prior to boiling.

Some great information on BIAB can be found at the below link

https://biabbrewing.com/

For my system, I chose BIAB as it was cheaper due to less equipment needed, more versatile and took up less space in my house. It utilizes a 20 gallon/80qt stainless steel stock pot with a 2000W 110v stainless steel heating element. For the BIAB setup, I use a polyester mesh (with a mesh size similar to a mosquito screen) that lines a stainless-steel strainer basket that came with the pot. The system I built also utilizes some of the HERMS/RIMS features by recirculating the mash constantly using a small 12v solar pump.

With this system, I can brew anywhere from 5 to 15 gallon worth of beer at a time. However, for anything larger than 10 gallons, you will be happier using a large 5500W heating element instead of my meager 2000W.

Step 2: Materials Needed

I will list the parts I used to create my brewing pot, you can use this list in your own quest of creating a brewing system or replicate my system. I will present the good, the bad, and “I wish I would have done that differently” I will go into details on sensors and electronics in the following sections. I am not affiliated with any of the links given below. I added them as a good starting point for your own research.

This is by no means a be-all-end-all list and you may notice a few additional items in the picture that aren’t listed (such as the tri-clamp port on the lid). I added these things for future expansion plans. Use the below list to design your dream system to suit you the best.

Tools needed

Step 3:

Step 4: Health and Safety

Having worked with industry for the past decade, health and safety have been drilled into my mind. Everyone wants to go home in one piece every day and spend time with your family and friends. Please make sure you take your time and are comfortable following any of the below steps.

Some of the tasks that you are going to perform when building a system like this involve some hazards you should be aware of. Please wear gloves and glasses when dealing with sharp edges. Also be mindful of the fire hazard and hazardous fumes during soldering. It is best to do this outdoors.

One important thing to note: be careful what type of glove you are wearing, you do not want to wear a synthetic glove when soldering. The glove will melt and burn you.

Please be smart and safe especially when dealing with fire and mains electricity. Your life is more important than brewing beer in a shiny pot!

Step 5: Putting It All Together

Once you end up with all the items you require, you can start enjoying the part of putting it all together. Since this process is based on an idea in your head, make sure to take your time and draw things out with pen and paper. Measure twice and cut once is not just a saying.

The first step I did after I received my pot was to scratch my head and ask myself, should I have bought a smaller pot? The dimension of my pot is 19 inches wide and 19 inches high. For all intents and purposes, you generally want a tall and narrow pot rather than a wide and short pot.

After I received all the parts, I grabbed a good metal drill bit to prepare pilot holes of where I wanted my pieces to eventually end up. The element needed to be as close to the bottom as possible to allow enough room for the basket and also to allow for the smallest amount of liquid in the pot.

I used olive oil in the area that I was going to drill and kept a spray bottle nearby. You will want to drill fast and with lots of pressure. Drill a few seconds then cool the pot with water. After I had a good enough depression I used a bi-metal hole saw to cut an appropriate size hole in the pot. Use a high speed and lots of pressure with the hole saw and use the depression you drilled earlier as a guide for your hole saw. Again, drill a few seconds, then cool everything down with water including the saw. This will help with the cut and the life of your tools.

In order to decide the correct size check the source of your parts. In the case of parts obtained from www.BrewHarware.com, proper dimensions are included in the description of the part. This will guide you to determine the proper size of the hole you will need. that you should be drilling for your fittings. If you are unsure, stick on the smaller end of things and get an accurate measurement of whatever you want to install.

After drilling the holes you need with either a hole saw or a step bit, you should take some sandpaper and a metal file/rasp to deburr the opening you just cut. This will make soldering easier and you won’t cut your self down the road when you are using your new brewing system.

After I cut all the holes, I installed the ball valve and thermowell first. These two fittings were no weld and simply need to be fed through the hole and secured with a nut and O-Ring. Generally, these fittings have one welded portion, a thread, an O-Ring, and a nut. The O-Ring should be placed on the outside of the pot between the pot and the welded portion of the fitting. If you were to place the O-Ring between the nut and the inside of the pot, you might have liquid find its way through the thread.

I won’t go into much detail on soldering stainless steel. If you have ever soldered plumbing in your house (with copper pipe), you should be ok. If you have not, you may want to consider practicing on some old pipe first before you attempt to solder your pot. If you look at my pictures you may notice that I should have practiced a bit more.

For a proper explanation and demonstration, I highly recommend you check out the below video by the owner of www.BrewHardware.com. I am not affiliated with his site in any way but have used his parts and tutorials to build my pot. I can highly recommend it to anyone else. His tutorial could fill an entire instructable on its own.

Once you are done soldering, you can get yourself "Bar Keepers Friend" Cleaner. This will help take off old flux and make your pot nice and shiny without the need for polishing equipment. After you have successfully installed all your fittings and soldered on your tri-clamp ferrule (and hopefully used less solder than I did), you are ready to install a heating element. Again, I will refer you to the owner of www.BrewHardware.com. He does a terrific job explaining the proper installation of a heating element and the enclosure he sells.

Long story short, your 120v mains power (in North America) will have three wires. Make sure your wire size and break are appropriately sized for the amount of power the heating element is going to use. If you use a 2000w heating element at 120v, you are looking at approximately 16 amps of power. You should be using a 20 amp breaker and a thick gauge wire to handle that load. Ask your friendly neighborhood electrician to help you out if you don’t know. Don’t set your house on fire while brewing.

To install your heating element, take the black and white wire and connect it to your heating element. The bare uninsulated wire is used as ground and is connected to the enclosure.

If you followed these steps and assembled everything to suit your needs, you have a barebones system that you can use to brew beer with. If you choose to simply plug your element in, you can heat your water and make delicious beer. If like me you’d like to kick back with a cool beverage in your hand and let the computer do the work for you, keep on reading. Now the fun part of my build starts.

Step 6: Sealing Your Pot

I designed my pot to be used as a fermentation vessel as well. In order to properly ferment beer without problems such as infections, I needed to create a seal. Searching the world wide web, I came across a custom Teflon seal. Simply cut out a cardboard ring and wrap it with Teflon tape. Make sure to overlay each lap with the tape by half. I could have used different materials but I had some Teflon tape laying around the house.

In order to keep your lid closed, you can use the spring loaded clamps (linked in the materials section) and attach them to your pot. You will need rivets that fit through your particular clamps and are made of stainless steel (because why not). Find a suitable metal drill for the rivets and start marking out the locations for your clamps. In my case, I placed the lid on the pot without the seal and placed the clamps where I thought they would work best. I spaced them out evenly.

After spacing them out, I started to drill each one individually. I used tape to hold the clamps in place and used the holes as a guide for my drill. Similar to cutting the holes for your element and valves, use high speed and pressure and cool down the drill bit and area with oil and water. after you drilled the two holes, use some sandpaper or the drill bit to de-burr the hole. Insert your rivet into the hole and secure it with the rivet gun. This will form a secure hold. you can use a small Dremel tool to cut off the rivet on the inside if they are too long. You can get different lengths of rivets, in my case they ended up being longer than I expected. I used a cut off wheel and a Dremel to trim them. Make sure to wear safety glasses when doing this, you don't want metal shards in your eye.

Step 7: Brewing Controller Parts

The brewing controller uses a few simple off the shelf components to control the brewing system. Below you will find the parts that I used to create my controller. Again, you can use this list as a guide for your own system.

You may have noticed, I did not include a link for an enclosure in the above list. The housing that I use for my controller is unique in the sense that I salvaged it from an old power bank that no longer worked. Ever since getting into the world of Arduino, I have become fascinated with all sorts of electronics. I am constantly on the lookout for new things to take apart and learn how they work. I would highly recommend checking your local second-hand stores or even recycling depots for all sorts of electronics. You can find some great components for all sorts of projects.

In my case, I was able to open the old power bank, determine that the battery no longer charged properly and recycled it. I salvaged the main PCB of the unit and learned how to de-solder components. I was able to re-use a push button and the USB connector. All the other passive components are now sitting in a bag until I learn how to use them. I can highly recommend a video on youtube by GreatScott that goes into great detail on what you should salvage and how to go about de-soldering it.

Step 8: Assembly – From Idea to Breadboard to Perfboard

Now that I had sourced my components, it was time to figure out how to connect it all. I will describe how I designed the circuit, tested it with a breadboard and ended up creating a perfboard that fit into my salvaged enclosure and utilized the passive components I rescued.

All the components needed for the brewing controller can be broken into several individual circuit blocks:

  1. The Temperature circuit
  2. The Fan/Pump circuit
  3. The SSR circuit
  4. The HC-05 Circuit

These blocks form the basis of the controller and can be expanded upon. The Arduino Uno hosts several additional unused pins that could be used for other components, sensors, etc. You are only limited by your imagination … and the available pins on the Arduino.

Below you will find the tools you require to replicate this work

  • Multimeter
  • Soldering Iron
  • Rosin Core Solder
  • Wire cutters
  • Thin wire
  • Nail clippers

You will also need to install the Arduino IDE. I will assume you have a little background in using the Arduino but if not, you can find all the relevant information on the Arduino website (https://www.arduino.cc/) include a helpful wiki, tutorials, a shop, and a friendly forum whose sole purpose is to tell you to use the search bar. You can find the Arduino software and other information on this website as well.

If you are using windows 10 (like a lot of you might be by now), you can find the Arduino IDE in the Microsoft store free of charge, simply add the app and off you go.

Step 9: The Temperature Circuit

Before I did any soldering, I sat down and figured out what I wanted the brewing controller to do. First and foremost, I needed it to control the temperature based on the exact temperature of whatever was in my pot. I set out to procure myself a very versatile DS18B20 temperature sensor. These sensors can be daisy-chained together and be read on one single pin on the Arduino you can read up to 8 sensors on the same pin (which I will explain in the sections below). This is useful if you plan on adding an additional sensor for other applications.

From the get-go of the project, I aimed to produce something semi-professional. My biggest pet peeve was the fact that I did not want to see my Arduino or utilize cheap male pin connectors used for breadboards. Instead, I ended up finding a few very cheap male to female USB cables. USB cables are based on 4 pins. Because the DS18B20 only uses three wires, you will have to choose which pins you utilize. You will also have to make sure that any future expansion will follow the same layout. I have provided a fritzing diagram for the temperature sensor.

The DS18B20 VCC pin is connected to 5v, the Ground pin to the Arduino ground and the Data pin to the pin of your choice on the Arduino. In order to read the data, a pull-up resistor needs to be connected between the VCC and Data pins. After that, you can connect up to 8 sensors in parallel to the same three pins.

Now to connect this sensor to your controller with a handy USB cable, cut the Male-Female USB cable to a suitable length. Strip back the insulation and you will likely see a shielded wire. This might be confusing if you see it the first time (it was for me). Shielded wire is simply several thin gauge wires wrapped in aluminum foil. Peel the shielding back and you will find four (4) thin wires. Strip the insulation on these very carefully. These wires are usually stranded wires and are easily cut.

Now that you have a male USB connector with stripped wires and a female end with stripped wires grab your trusty multimeter. If you don’t have one, get one right away if you are at all serious about electronics. They are invaluable tools. Set the meter to continuity mode and start testing the wires. If you look inside the USB connector, you will see 4 metal pins, touch one of your multimeter probes to one of the pins and use the second probe and start touching each wire. If you find the wire that is connected to the pin, the multimeter will beep. Now you can note down which pin belongs to which connector.

After you have repeated the above step for each pin, you can decide which pins you would like to use. Make a clear diagram of the connector so you are not confused later if things don’t work. After you decided the pin layout, take your female end and pigtail the corresponding wires (for example RED for VCC, White for Data, Green for Ground). Use a soldering iron and some solder to tin each pigtail. Tinning the cables simply means adding a drop of solder to each one so it will be easier to connect later. You can cut off the unused pin as it will not be used.

Now that you have selected your layout, grab your DS18B20 sensor and connect the wires as per your layout (for example VCC to your RED Pigtail, Data to your White pigtail, and Ground to your Green pigtail). Solder each connection. Depending on where you bought your sensor, the cables may already be tinned. If they are not, repeat the step above and connect everything together. At this point, you can choose to leave it open for the time being or shrinkwrap the setup. If you choose to leave it open, you can later go back and double check the connections.

If your system utilized additional DS18B20, you can repeat the above step for each of the sensors. To make your life easier and make sure you use the correct layout, simply plug in your new cable into either the male or female end of the cable you have previously created and use a multimeter to test each cable. Not every cable is created equal and you may not find the same color layout.

Now that you have the cables ready, you can connect everything using a breadboard and test it out. Choose a pin on the Arduino board and connect the data cable of your sensor(s) to this pin, connect the VCC pin to your 5v Arduino output and your ground to one of the Arduino ground pins. Add a 4.7k pull-up resistor between your data wire and VCC and open your Arduino IDE.

If you are setting up one (1) DS18B20, you can skip the following step. For using multiple DS18B20 sensors, you will need to obtain their physical address to read their respective temperatures. Each sensor has a 16-bit unique hex code that identifies the sensor in your daisy chain.

In order to find each sensors address and give it a meaningful name in your program you can run the following sketch with your Arduino:

<p>#include  // the library that is needed to read the sensors<br>OneWire  ds(4);  // Connect your 1-wire device to pin 4	
<br></p><p>void setup(void) {
 Serial.begin(9600);
 discoverOneWireDevices(); // when the Arduino starts call this function
}
<br></p><p>void discoverOneWireDevices(void) {
 byte i;
 byte present = 0;
 byte data[12];
 byte addr[8];
 Serial.print("Looking for 1-Wire devices...\n\r");
 while(ds.search(addr)) {
   Serial.print("\n\rFound \'1-Wire\' device with address:\n\r");
   for( i = 0; i < 8; i++) {
     Serial.print("0x");
     if (addr[i] < 16) {
       Serial.print('0');
     }
     Serial.print(addr[i], HEX);
     if (i < 7) {
       Serial.print(", ");
     }
   }
   if ( OneWire::crc8( addr, 7) != addr[7]) {
       Serial.print("CRC is not valid!\n");
       return;
   }
 }
 Serial.print("\n\r\n\rThat's it.\r\n");
 ds.reset_search();
 return;
}
<br></p><p>void loop(void) {
 // nothing to see here
}</p>

A complete tutorial
on the above code is given in the following link:

http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html

Now that you have found the unique address for each of your sensors, simply create a sketch to read the temperature. First import the OneWire and DallasTemperature libraries. Following this, define the pin you are using on the Arduino in the following way:

<p>#include <br>#include 
#define ONE_WIRE_BUS 4 // 4 refers to pin 4 and can be changed to suit your needs.</p>

Now you are ready to define your device(s) variable(s) using the following statement:

<p>DeviceAddress devA = {0x28, 0xFF, 0x0E, 0x08, 0xA3, 0x16, 0x03, 0x40}; </p><p>//change the value in the brackets to whatever your sensors address is</p>

Declare this
variable before your void setup () function. After you declared your variable(s), use either a generic function like

<p>Serial.println(sensors.getTempCByIndex(0));</p>

or a specific one like:

<p>Serial.print(sensors.getTempC(devA));</p>

In your void loop() or another method. You can either get temperatures for each sensor in the order they are connected (first statement) or based on their physical address (second statement).

Step 10: The Fan/Pump Circuit

This circuit is likely the easiest to work out. The positive wires for the fan and the pump are connected to a 12v relay. The relay itself has two (2) channels and is powered by 5v. Connect your 5v power of the Arduino to VCC on your relay. Do the same for the ground connection. Now choose two (2) pins that you would like to use to control each channel of the relay. These relays are normally open meaning that if your input signal is set to LOW, it will turn the relay on. Once you switch your signal pin to HIGH, it will turn off.

Now with this knowledge hook up the wires and set the pins to LOW or to HIGH depending on what you would like to do.

<p>Int relayPin = 3; //setup your pin, now you can refer to relayPin in your code<br>pinMode(relayPin,Output); //sets pin #3 to output a signal
digitalWrite(relayPin, LOW); // signals the relay to turn off
digitalWrite(relayPin, HIGH); // signals the relay to turn on</p>

Step 11: The Solid State Relay Circuit

The previous relay operation is very similar to the solid state relay we are utilizing. The relay has four (4) terminals, two (2) AC current terminals (this will be your hot wire) and two (2) DC input voltage terminals. The input voltage can vary depending on the relay you purchased; however, it is likely going to be in the range of 3-32v. This means that a 5v signal from any of our Arduino pins will trigger the relay.

Since the relay is going to be used to precisely control the temperature of whatever you are heating in your pot, it will have to get throttled. Because we are dealing with AC current, we have to rely on repeatedly turning the relay on and off to get to our desired temperature. This on/off cycle is powered by a PWM signal from your Arduino.

A PWM or Pulse Width Modulation signal is an on/off cycle repeated really fast depending on your input. You can set the PWM signal anywhere from 0 to 255. If you wanted to set your relay to turn on with a 25% duty cycle, you essentially would turn on the relay for 1-time unit and leave it off for 3 (1/4 time units). Similarly, if you wanted a 75% duty cycle, you would turn your relay on for 3-time units and off for 1 (3/4 time units). By setting the relay anywhere between 0 and 255 you can set your desired on/off duty cycle and either let more or less power run through your heating element. In your Arduino code, simply do the following:

<p>int PinRelay = 3; //choose a PWM pin (squiggly line) <br>pinMode(PinRelay,OUTPUT); //pin for relay
analogWrite(PinRelay, 255); //full 100% duty cycle, change 255 to anything 0-255</p>

Step 12: The HC-05 Circuit

The HC-05 is a cheap Bluetooth module you can utilize to send and receive information to and from your Arduino. To hook up the HC-05, simply connect VCC to your 5v Arduino pin, Ground to the Arduino Ground, and TX of your HC-05 to the RX Pin on the Arduino. The RX pin on the HC-05 gets run through a voltage divider to the TX pin on the Arduino.

The voltage divider is created by connecting the RX of the HC-05 to the TX pin on your Arduino with a 1K Ohm resistor. The RX line gets connected to Ground using a 2K Ohm resistor prior to entering the 1K Ohm resistor (the diagram will make this a lot clearer to understand).

Now that you have connected the HC-05, you can pair your device with your computer/phone of your choice. The default name will be HC-05 with the default password of 1234. If you wanted to change these values, you could enter the AT command mode using the Arduino IDE. Once the HC-05 is paired, you can start sending and receiving serial commands and information. This is where the next section, the Universal Windows App created using Microsoft’s Visual Studio 2017 in C#.

Before going further, whenever you try to upload code to your Arduino, make sure you have the RX and TX pins disconnected. If they are connected, you will be greeted with a bunch of error messages by the Arduino IDE.

Step 13: Thoughts on PCBs

Now that I had it all soldered up on a perfboard and fit it into my enclosure, I started thinking about creating a PCB or printed circuit board. I was mostly done with my controller and have started thinking about the PCB design; however, with any rabbit hole you enter, you find yourself looking for things to add. There are a few more functions that I want to add before finalizing a PCB.

For your project, this might be enough features. There are some great tutorials on instructable that go into detail of how to create a PCB. One could use Fritzing, Eagle or even an online service as EasyEDA.

EasyEDA lets you create your wiring diagram, PCB layout and order your parts and PCBs in a few simple steps. A great tutorial that delves into this deeper is given by GreatScott in this youtube video:

I am not affiliated with any of the links or the channel but think he does a great job explaining everything to give you enough of a starting point to figure it out for your particular situation.

Step 14: The Structure of the Brain

Before we get too deep into the programming aspect of things, I want to establish the basic principle of how we communicate between a computer and a phone running Windows 10. I set out designing my system to be able to remotely operate it.

I wanted to be able to collect a dizzying amount of data to be able to recreate processes as precisely as possible. This would not necessarily make better beer, but it would allow me to graph things and get a better handle of what is going on.

Given the fact that I have worked with Microsoft for decades, I am very comfortable using software and programming. When Windows 10 was released, I was mesmerized by Microsoft’s Remote Arduino and Virtual Shield for Arduino. I set out to find myself a used Lumia 635 on eBay and installed windows 10 on it.

After installing Windows 10 on the phone, I had to look at creating an application for it. I was surprised to find that not a lot of people seem to be interested in using a windows 10 phone. There was little information available.

I set up a program that would talk to the Arduino using a Bluetooth serial connection. The basic concept of my software is to send a command to the Arduino, process the command in the Arduino and execute accordingly.

Commands are sent to the Arduino in the following format:

As an example, if I wanted to set my relay to a certain PWM setting, I would send the following command to the Arduino:

<1,255> // 1 refers to the first program and 255 refers to the value you sent to the Arduino.

Now, let us start getting into the nitty-gritty on the Arduino code. I have uploaded the code for this project to GitHub at the below link:

You can find a full version of the Arduino code along with the UWP app and a WPF (for pre-Windows 10 systems).

Step 15: The Brain: the Arduino Code

The Arduino portion of the code is fairly straightforward. We only need two main functions to deal with commands that come from your computer or phone.

The first thing we need to do is create the functions that we want to run with the Arduino. The naming of these can be whatever you choose, in this case, I have created 5 generic commands called brewCommand 1 through 5

<p>Void brewCommand1()<br>{
Do something
}
……..
Void brewCommand5()
{
Do something
}</p>

After declaring all your commands, we need to
create an array function. We create this array function by calling

void (*Functionname[])() { … }

You can find an example in the code below.

<p>//Create array to select different functions elegantly<br>void (*brewCommands[])() =
{
  brewCommand1, //turn on/off pump
  brewCommand2, //turn on/off fan
  brewCommand3, //get temperature
  brewCommand4, //set PWM for relay
  brewCommand5, //test command
};</p>

Now that we have told the Arduino what we want to
do, we have to tell it to listen constantly for these commands on the serial input. We do this in the loop function. We call the first main function

recvWithStartEndMarkers();

<p>void loop() {<br>  // put your main code here, to run repeatedly:
  recvWithStartEndMarkers();
  if (newData == true) {
  strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            // because strtok() used in parseData() replaces the commas with \0
  parseData();
  brewCommands[caseSelect - 1]();
  delay(100);
  newData = false;
}</p>

In the function recvWithStartEndMarkers() below, we
define a few variables first. These variables define what we want the start and end markers of our function to be. In our case, the start of a command is defined by “<” and the end by “>”. Some of the other variables are used to help us deal with incoming data.

recvInProgress is a Boolean that will tell us if we are currently receiving data. Data in the serial monitor is received byte for byte in the Arduino. So a command like <1> will be received and read by the Arduino one character at a time. This is done in the while loop. During this loop, the Arduino receives all the characters in the command and compares them to the start and end markers. If we find the start marker, the program knows to keep looping through the characters until we find the end marker.

When a new command arrives, the loop will detect the start marker and start loading a character array with each character. This array called receivedChar[] will have a new character appended to until we find the end marker.

<p>// when command is received as "<xxx,xxx>" this code will take pick out the right characters<br>void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();</xxx,xxx></p><p>        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }
        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}</p>

Now that we have received
our command, we can look at our main loop and see that we are saving our received command in a temporary variable prior to calling parseData()

This function is fairly straightforward. We take our received command in a string format and break it using a comma. strtokIndx = strtok(tempChars, “,”) uses the command and takes the first portion of the tempChars variable before the comma. The caseSelect = atoi(strtokIndx) line converts the string to an actual value.

For example, if we sent “<1,2>”, the first line would turn the command into “<1” and the second line would convert it to an integer “1”. After determining the first portion of the command, we repeat the step but we add a NULL to the statement, which simply looks at the second portion of the command “2>”, which then gets turned into an integer “2” using the atoi function.

<p>// split the data into its parts<br>void parseData() 
{      
    char * strtokIndx; // this is used by strtok() as an index</p><p>
    strtokIndx = strtok(tempChars,",");
    caseSelect = atoi(strtokIndx);</p><p>
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    caseValue = atoi(strtokIndx);     // convert this part to an integer
}</p>

After receiving our command,
we now have a command select number and a command value (should one be needed such as a PWM signal output). With this information, we can select the correct brewcommand by calling brewCommands[caseSelect -1](). We have to subtract 1 as our array starts counting at 0.

After we reset all our variables, we can start over again. Now we can go on to the last step of this instructable, the C# UWP application.

Step 16: The Brain: the UWP App

While programming the Arduino Sketch may be a simple task, getting your windows 10 machine to talk to it via Bluetooth is a little trickier. I will assume you will have a little bit of background in programming and a basic understanding of how to write code in C#. If you are not, there are great tutorials out there on the world wide web to get you started.

I am by no means an expert when it comes to this but have a good enough grasp of how things work to be dangerous. You will find lots of code snippets on the web that you can adapt in all sort of ways to what you eventually want to do.

The key aspect of the app as described in the above sections is to send and receive commands and data. In my example, I am using a Bluetooth connection to read the incoming serial data and send commands via serial.

Before we get started you will need to obtain the following:

Visual Studio 2017 Community - https://visualstudio.microsoft.com/downloads/

Download the free version and install it. You will be faced with a dizzying amount of options as to what to install. During the installation, you will have the choice to install packages and individual components. After selecting the core packages, go into the individual components section and look for VC C++ entries. If they are unselected you might or might not need them. I have had to play with these options many times and haven’t quite wrapped my head around which ones actually make my app work. I suggest you download the source code and see if opening the file gives you errors. If you do see errors, adding some of these individual components will solve the problems.

Now that you have Visual Studio hopefully set up properly, open it up and create a new Universal Windows (Blank App). At this point, I will not create a “Hello World” example and go straight into the nitty-gritty. I will explain the key aspects and let you read into the source code further adapt it to whatever you are envisioning.

Before we can utilize the Bluetooth function in windows, we have to declare it in the Apps manifest. Right-click on Package.appxmanifest and select “View Code”. At the very bottom of the code, we have to add Bluetooth to the capabilities section like the following code. Note that since Instructables is based on HTML, posting XAML code doesn't work very well, simply add "<" at the beginning of each line and ">" at the end for the following lines:

<p>1. Capabilities<br>2.     Capability Name="internetClient" /
3.     DeviceCapability Name="bluetooth.rfcomm"
4.       Device Id="any"
5.         Function Type="name:serialPort" /
6.       /Device
7.     /DeviceCapability
8. /Capabilities</p>

Now that we have these capabilities, we have to obtain all paired Bluetooth devices. We do this by creating a public class that holds the ID and Name and pass in all available paired devices to it like this:

<p>DeviceInformationCollection DeviceInfoCollection = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));</p>

The RfcommDevice Class in
the code will retrieve all the information we need to connect to the device. We call this function and add it to a list in this function:

<p>InitializeRfcommDeviceService()</p>

After we
have all devices we need to pass the connection information for our selected device to a new RfcommDeviceService object.

<p>_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id);</p>

Using the above statement we
pass the ID information from our selected list item to our object.

<p>await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);</p>

Now that we have connected
to one of our devices, we can start listening to what it is telling us. To do that, we need to create an async void Listen (). An async method runs simultaneously with your app. This will not bog down your interface due to heavy computing and utilizes more than just one core of your processor (unlike the Arduino).

We can start the listening process by using some simple code like this:

<p>ReadCancellationTokenSource = new CancellationTokenSource();<br>                if (_socket.InputStream != null)
                {
                    dataReaderObject = new DataReader(_socket.InputStream);
                    while (true)
                    {
                        await ReadAsync(ReadCancellationTokenSource.Token);
                    }
                }</p>

The datareader object will hold all the text that is being transmitted to the computer in the connected devices socket inputstream. It will continuously call the next function (while we have an open connection).

We can now call the Async task of reading the inputstream

<p>private async Task ReadAsync(CancellationToken cancellationToken)<br>	    Task<uint32> loadAsyncTask;</uint32></p><p>            uint ReadBufferLength = 1024;</p><p>            // If task cancellation was requested, comply
            cancellationToken.ThrowIfCancellationRequested();</p><p>            // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
            dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;</p><p>            // Create a task object to wait for data on the serialPort.InputStream
            loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);</p><p>            // Launch the task and wait
            UInt32 bytesRead = await loadAsyncTask;
            if (bytesRead > 0)
            {
                try
                {
                    string recvdtxt = dataReaderObject.ReadString(bytesRead);
                    System.Diagnostics.Debug.WriteLine(recvdtxt);
                    this.textBoxRecvdText.Text += recvdtxt;
                    currentReceivedBytes = recvdtxt;
                    lastReceivedBytes = lastReceivedBytes + currentReceivedBytes;
                    if (lastReceivedBytes.Contains("\r\n") == true)
                    {
				Do work
                    }	</p><p>                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ReadAsync: " + ex.Message);
                }</p><p>            }
        }</p>

In this task, we define the maximum length of the readbuffer. It will store information in this buffer until it reaches the end and gets overwritten. The Serial output of the Arduino transmits information byte for byte. We capture this information by creating a string variable recvtxt to pass all the currently read bytes to it using dataReaderObject.ReadString(bytesRead).

Because the Arduino and the computer are not operations in sync, we have to pass the bytes to some temporary variables. We can use two public string variables defined outside the method to hold all the text that was received in the last read and pass it to another string variable that combines the last read bytes with the current bytes. This will eventually form a full line that we can decipher.

Since we know that data is printed on a new line in the serial monitor, we can simply look for the new line serial command “\r\n”, we can call an if statement to perform a certain task with the information we have received. For example, if we received “23.4,23.9\r\n” to represent two measurements of temperature, we can take the entire line and separate it into two variables based on the “,”. You have to remember that the line is a string, so you will have to convert it to an integer or double variable before you can do anything with it.

Now we can figure out how to send an instruction to the Arduino. We first need to create a datawriter object and tell it to write data to the device we have selected (the socket variable). After we have created the object, we call the Async function WriteAsync() and pass off our message to the Arduino in a String format .

<p>dataWriterObject = new DataWriter(_socket.OutputStream);</p><p>//Launch the WriteAsync task to perform the write</p><p>await WriteAsync(msg);</p>
<p>private async Task WriteAsync(string msg)<br>        {
            Task<uint32> storeAsyncTask;</uint32></p><p>            if (msg == "")
                msg = "none";// sendText.Text;
            if (msg.Length != 0)
            
            {
                // Load the text from the sendText input text box to the dataWriter object
                dataWriterObject.WriteString(msg);</p><p>                // Launch an async task to complete the write operation
                storeAsyncTask = dataWriterObject.StoreAsync().AsTask();</p><p>                UInt32 bytesWritten = await storeAsyncTask;
                if (bytesWritten > 0)
                {
                    string status_Text = msg + ", ";
                    status_Text += bytesWritten.ToString();
                    status_Text += " bytes written successfully!";
                    System.Diagnostics.Debug.WriteLine(status_Text);
                }
            }
            else
            {
                string status_Text2 = "Enter the text you want to write and then click on 'WRITE'";
                System.Diagnostics.Debug.WriteLine(status_Text2);
            }
        }</p>

In the WriteAsync() method, we can simply use the following statement dataWriterObject.WriteString(msg);

This statement will write a new line in the Arduino’s Serial. After writing the command or message, we complete the operation with storeAsyncTask = dataWriterObject.StoreAsync().AsTask();

That is all there is to it. Everything beyond that is fluff that you can adjust to your heart's content to make it suit your personal needs. I have attached the source code that should be helpful enough so everyone can figure out how to implement it in their own programs.

Step 17: Future Expansion

If your eyes haven't glazed over by now, you are probably thinking "what else could you possibly want"? The short answer, the possibilities are endless. Below are a few items that would be great to add:

  • A water level and specific gravity meter
  • motorized ball valves to open and close the valve
  • Additional sensors for temperature
  • An updated graphical user interface
  • Beer recipe calculator
  • etc ...

The sky is the limit. I hope you enjoyed this instructable and could find a few helpful tidbits of information in it to help you out with your project. Working with electronics on this project has given me an appreciation of how the world works. I'd love to see how you end up using this information!

Cheers, and stay safe! Now it is time to make some beer!

First Time Author

Participated in the
First Time Author