• Home
  • Services
  • Pricing
    • Website costs
    • Support Costs
  • Case Studies
  • Blog
  • About
    • About Us
    • FAQ
    • Terms and Conditions
Backbone BackBoneWebDesigns logo.Creating websites to build on your investment.

Backbone Web Designs

Contact Us
  • Home
  • Services
  • Pricing
    • Website costs
    • Support Costs
  • Case Studies
  • Blog
  • About
    • About Us
    • FAQ
    • Terms and Conditions
Home >> technical >> brewPi Remix Uno wifi ATmega328P+ESP8266

brewPi Remix Uno wifi ATmega328P+ESP8266

Last Updated on June 17, 2026 by Colin Slater

I was following this post to setup a brewpi remix arduino with an on-board ESP8266 using firmware version 0.2.13, which as far as I can tell is the last version of brewpi remix legacy that you can run on Arduino. In order to get this to work for me I had to adapt them a little to use esptool (as Im running on Linux) and use different code to properly handle communications over a TCVP socket as well as usb.

But wait. Before you take a read, be aware although I once was, I am no longer a developer and I claim no python skills whatsoever as its quite new to me. I may also not be doing this the right way. And someone may well have done this before but a better way.

All I’m saying is in the time I had available between searching the internet, reading and then coding and testing, this is how I ended up with a working brewpi legacy running over wifi to my Ubuntu Studio web server. I found it quicker than trawling through various scattered posts to find an answer for an old an unsupported product.

I also take no credit whatsoever for the code, all of which belongs to the original authors you will find on the brewPi website. Very clever and generous folk. You should be following that guide, which is all Ive done, unless you are stuck needing modified code to connect to the ESP, or cant find how to use esptool. Pretty much, noone should need this page, so what are you doing here?

Anyway, I managed to get this running by flashing the arduino Uno, after correctly setting the jumper switches (00110000), with:

avrdude -F -e -p atmega328p -c arduino -b 115200 -P /dev/ttyUSB1 -U flash:w:"/home/master/brewpi-tools-rmx/brewpi-arduino-uno-revc-0.2.13+da7e14a9.hex" -C /usr/share/arduino/hardware/tools/avrdude.conf

After resetting the jumpers to address the esp8266 (00001110) I used esptool to flash the following:

esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -fs detect 0x00000 boot_v1.4\(b1\).bin 0x1000 user1.bin 0x3FC000 esp_init_data_default.bin 0x3FE000 blank.bin 

Those settings worked for an 8Mb board, but with -fs detect I would expect it to work with other boards. Obviously you’ll need to use the file locations and arduino IDE setup for your system. You can get the latter by using the IDE to download a blank program and grap the avrdude syntax from the output logs.

After correctly setting the jumpers (11000000) and setting up the wifi as per esp_link instructions, I was left with a working arduino connected to wifi, but with no web interface because the script wont connect as it stands. As the guide notes explain, changes to the python code are required. That’s because it cant handle an IP socket when you change the port in config.cfg to reflect the IP address of the esp8266.

I modified the code to add this support, which includes some additional string processing and error handling I found necessary. The code requiring an update is backgroundserial.py in the brewpi directory. I replaced its contents with this:


#!/usr/bin/env python3

# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy)

# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX).
#
# BrewPi Script RMX is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# BrewPi Script RMX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BrewPi Script RMX. If not, see <https://www.gnu.org/licenses/>.

# These scripts were originally a part of brewpi-script, a part of
# the BrewPi project. Legacy support (for the very popular Arduino
# controller) seems to have been discontinued in favor of new hardware.

# All credit for the original brewpi-script goes to @elcojacobs,
# @m-mcgowan, @rbrady, @steersbob, @glibersat, @Niels-R and I'm sure
# many more contributors around the world. My apologies if I have
# missed anyone; those were the names listed as contributors on the
# Legacy branch.

# See: 'original-license.md' for notes about the original project's
# license and credits. */
# Standard Imports
import threading  # for running the serial read in a separate thread
import queue  # for handling the serial read and write operations in a thread-safe manner
import sys  # for system-specific parameters and functions
import time  # for handling time-related operations
import socket  # for creating socket connections

# BrewPi specific imports
from BrewPiUtil import printStdErr, logMessage  # utility functions for logging and error handling
from serial import Serial, SerialException  # for handling serial communications
from expandLogMessage import filterOutLogMessages  # for filtering log messages
from packaging.version import Version  # for version handling
import BrewPiUtil  # additional utility functions

# Class for handling socket serial communication
class SocketSerial:
    def __init__(self, host, port):
        self.host = host  # Host address
        self.port = port  # Port number
        self.sock = None  # Socket object
        self.open()  # Open the socket connection

    def open(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Create a TCP/IP socket
        self.sock.connect((self.host, self.port))  # Connect to the specified host and port
        self.sock.settimeout(2)  # Set timeout for read/write operations

    def close(self):
        if self.sock:  # If socket is open
            self.sock.close()  # Close the socket

    def write(self, data):
        self.sock.sendall(data.encode('utf-8'))  # Send data encoded in UTF-8

    def read(self, size=1024):
        return self.sock.recv(size)  # Receive data from the socket

    def readline(self):
        data = b''  # Initialize data buffer
        while True:
            chunk = self.read(1)  # Read one byte at a time
            if chunk == b'\n':  # If newline character is found, break the loop
                break
            data += chunk  # Append byte to data buffer
        return data.decode('utf-8', errors='replace')  # Decode buffer to string

    def isOpen(self):
        try:
            self.sock.send(b'')  # Try to send empty byte
        except socket.error:
            return False  # If error occurs, socket is not open
        return True  # If no error, socket is open

# Class for handling background serial communication
class BackGroundSerial:
    def __init__(self, serial_port):
        self.buffer = ''  # Data buffer
        self.ser = serial_port  # Serial port object
        self.queue = queue.Queue()  # Queue for storing read lines
        self.messages = queue.Queue()  # Queue for storing log messages
        self.thread = None  # Background thread
        self.error = False  # Error flag
        self.fatal_error = None  # Fatal error message
        self.run = False  # Run flag

    def start(self):
        self.ser.write_timeout = 2  # Set write timeout
        self.run = True  # Set run flag to true
        if not self.thread:  # If thread is not already running
            self.thread = threading.Thread(target=self.__listenThread)  # Create new thread for listening
            self.thread.setDaemon(True)  # Set thread as daemon
            self.thread.start()  # Start the thread

    def stop(self):
        self.run = False  # Set run flag to false
        if self.thread:  # If thread is running
            self.thread.join()  # Wait for the thread to terminate
            self.thread = None  # Clear the thread reference

    def read_line(self):
        self.exit_on_fatal_error()  # Check for fatal errors
        try:
            return self.queue.get_nowait()  # Return next line from queue if available
        except queue.Empty:
            return None  # Return None if queue is empty

    def read_message(self):
        self.exit_on_fatal_error()  # Check for fatal errors
        try:
            return self.messages.get_nowait()  # Return next message from queue if available
        except queue.Empty:
            return None  # Return None if queue is empty

    def writeln(self, data):
        self.write(data + "\n")  # Write data followed by newline

    def write(self, data):
        self.exit_on_fatal_error()  # Check for fatal errors
        if not self.error:  # If no error
            try:
                if hasattr(data, 'encode'):  # If data is a string
                    self.ser.write(data.encode(encoding='cp437'))  # Encode data and write to serial port
                else:
                    self.ser.write(data)  # Write data directly if not a string
            except (IOError, OSError, SerialException, socket.error) as e:
                logMessage('Serial/Socket Error: {0})'.format(str(e)))  # Log the error
                self.error = True  # Set error flag to true

    def exit_on_fatal_error(self):
        if self.fatal_error is not None:  # If a fatal error has occurred
            self.stop()  # Stop the background serial communication
            logMessage(self.fatal_error)  # Log the fatal error
            if self.ser is not None:  # If serial port is open
                self.ser.close()  # Close the serial port
            del self.ser  # Delete the serial port reference
            sys.exit("Terminating due to fatal serial/socket error")  # Exit the program

    def __listenThread(self):
        lastReceive = time.time()  # Timestamp of last received data
        while self.run:  # While the run flag is true
            in_waiting = None  # Initialize in_waiting variable
            new_data = None  # Initialize new_data variable
            if not self.error:  # If no error
                try:
                    in_waiting = self.ser.readline()  # Read a line from serial port
                    if in_waiting:  # If data is received
                        new_data = in_waiting  # Set new_data to received data
                        lastReceive = time.time()  # Update lastReceive timestamp
                except (IOError, OSError, SerialException, socket.error) as e:
                    logMessage('Serial/Socket Error: {0})'.format(str(e)))  # Log the error
                    self.error = True  # Set error flag to true

            if new_data:  # If new data is received
                if isinstance(new_data, bytes):  # Ensure new_data is decoded to string
                    new_data = new_data.decode('utf-8', errors='replace')
                self.buffer = self.buffer + new_data  # Append new_data to buffer
                line = self.__get_line_from_buffer()  # Extract line from buffer
                if line:
                    self.queue.put(line)  # Add line to queue

            if self.error:  # If an error has occurred
                try:
                    self.ser.close()  # Close the serial port
                    self.ser.open()  # Reopen the serial port
                    self.error = False  # Clear the error flag
                except (ValueError, OSError, SerialException, socket.error) as e:
                    self.ser.close()  # Close the serial port
                    self.fatal_error = 'Lost serial/socket connection. Error: {0})'.format(str(e))  # Set fatal error message
                    self.run = False  # Set run flag to false

            time.sleep(0.01)  # Sleep for a short time to avoid high CPU usage

    def __get_line_from_buffer(self):
        while '\n' in self.buffer:  # While there is a newline character in the buffer
            stripped_buffer, messages = filterOutLogMessages(self.buffer)  # Filter out log messages
            if len(messages) > 0:  # If there are messages
                for message in messages:
                    self.messages.put(message[2:])  # Remove "D:" and add to queue
                self.buffer = stripped_buffer  # Update buffer
                continue
            lines = self.buffer.partition('\n')  # Partition buffer at newline character
            if lines[1] == '':  # If no newline character found
                self.buffer = lines[0]  # Update buffer
                return None
            else:
                self.buffer = lines[2]  # Update buffer
                return self.__asciiToUnicode(lines[0])  # Convert line to Unicode and return

    def __asciiToUnicode(self, s):
        return BrewPiUtil.asciiToUnicode(s)  # Convert ASCII string to Unicode

def setupConnection(config):
    port = config.get('port')  # Get port from config
    if port.startswith('socket://'):  # If port is a socket
        host, port = port.split('://')[1].split(':')  # Parse host and port
        port = int(port)  # Convert port to integer
        return SocketSerial(host, port)  # Return SocketSerial object
    else:
        return Serial(port, baudrate=57600, timeout=10)  # Return Serial object with specified parameters

if __name__ == '__main__':
    import simplejson  # For handling JSON data
    import time  # For handling time-related operations
    import BrewPiUtil as util  # Additional utility functions

    config_file = util.addSlash(sys.path[0]) + 'settings/config.cfg'  # Config file path
    config = util.readCfgWithDefaults(config_file)  # Read config with defaults
    ser = setupConnection(config)  # Setup connection

    if not ser:  # If connection setup fails
        printStdErr("Could not open Serial/Socket Port")  # Print error message
        exit()  # Exit the program

    bg_ser = BackGroundSerial(ser)  # Create BackGroundSerial object
    bg_ser.start()  # Start background serial communication

    success = 0  # Initialize success count
    fail = 0  # Initialize fail count

    for i in range(1, 5):  # Loop 4 times
        bg_ser.write('v')  # Write 'v' to serial port
        bg_ser.write('v')  # Write 'v' to serial port
        bg_ser.write('v')  # Write 'v' to serial port
        bg_ser.write('v')  # Write 'v' to serial port
        bg_ser.write('v')  # Write 'v' to serial port

        line = True  # Initialize line variable
        while(line):  # While line is True
            line = bg_ser.read_line()  # Read line from background serial
            if line:  # If line is not None
                if line[0] == 'V':  # If line starts with 'V'
                    try:
                        decoded = simplejson.loads(line[2:])  # Decode JSON from line
                        print("Success")  # Print success message
                        success += 1  # Increment success count
                    except simplejson.JSONDecodeError:
                        logMessage("Error: invalid JSON parameter string received: " + line)  # Log error message
                        fail += 1  # Increment fail count
                else:
                    print(line)  # Print the line

        time.sleep(5)  # Sleep for 5 seconds

    print("Successes: {0}, Fails: {1}".format(success, fail))  # Print summary of successes and fails
« Miele Tumble Dryer Clean out Airways

From our blog:

Miele Tumble Dryer Clean out Airways

So, you’ve cleaned the filters. You still get “clean out airways”? Do you have a Miele Condensing tumble dryer that keeps stopping with a “clean out airways” error? Have you cleaned out the three filters around the door of the drum, as per the manual? Have you washed and cleaned the internal plinth filter as […]

 [Read More]  
Puzzled man looking at website homepage

Tips for an effective homepage

Homepage Design Is it obvious what your business does from your homepage? Have you ever gone onto a website and wondered what is the product or service the website is supposed to be offering? I find it really frustrating when I have to look around a site to understand what it’s about. Too often I […]

 [Read More]  

Are website templates bad for web design?

Generally speaking a template is a starting point for a design. While many web designers may say they do not use templates, they most certainly do not write every line of code from scratch. They all use a “starting point” based on previous work. Anyone who says they don’t is, frankly, misleading you. It might […]

 [Read More]  
wordpress content management system

Why use wordpress?

Choosing the right platform to build a website. Regardless of whether you are building your own website, or getting a website professionally built for you, the choice of platform on which it runs is important. Choosing the wrong platform could lock you into a proprietary system, or one that lacks features you may need in […]

 [Read More]  
salesman with speech bubble saying welcome Im passionate about by job

Your website is your salesman

How do you make buying decisions? Have you ever bought a car because the salesman loves his job and has been in business for a long time? Did a large and impressive picture on the wall help you get out your wallet? Did a speech about his qualifications and job history get you over the […]

 [Read More]  
seo in 2015, person leaning on SEO in large letters

SEO in 2015

Search Engine Optimisation is a broad term. It’s often misused. Anything you do to your website that improves its importance as viewed by search engines can be called SEO. That encompasses a lot. From simple things like copying and rewriting blogs from other sites, responsive design, improving site load speed, to the number of words […]

 [Read More]  

Find us on facebook or Google+

terms and conditions

BackboneWebDesigns
ABN 49 517 735 203
Located Sydney Northern Beaches, Australia

Copyright © 2026 · backbone on Genesis Framework · WordPress · Log in