Python Hottop Client¶
pyHottop gives you the power to control your Hottop KN-8828b-2k+ roaster directly through python code. This library is meant to be used within applications and should not be used by itself to conduct a roast. Questions, comments or for support needs, please use the issues page on Github.
Features¶
This library provides full control of the Hottop roaster. Built-in callback functionality allows you to build applications that decouple the processing logic from the library from the core of your application.
- Stream Hottop readings
- Easy-to-use callbacks that return readings
- Adjustable polling interval
- Human-readable settings
- Flexible collection of data
- Debugging interface
- Control the Hottop directly
- Heater settings
- Fan speeds
- Drum motor toggle
- Cooling motor toggle
- Solenoid (drum door) toggle
- Chaff tray (detection) reader
- Auto-discover roaster connection
- Loops over USB connections to find the proper serial
Code Documentation¶
Getting Started¶
In order to interact with your Hottop roaster, you need to ensure your model has a USB-serial port which comes standard with the KN-8828b-2k+.
- Install the CP210x USB driver to read from the serial port:
https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers
- Install the pyHotop module:
pip install pyhottop
or python setup.py install
- Plug your Hottop roaster into your laptop.
- Test connectivity to the roaster by running the diagnostic utility:
pyhottop-test test
Code Documentation¶
Hottop Interface¶
This is the primary class that you will interact with when building applications for the Hottop roaster. This class will automatically spawn a threaded instance of the control process which will handle the core of the operations against the roaster.
-
class
pyhottop.pyhottop.
Hottop
[source]¶ Object to interact and control the hottop roaster.
Returns: Hottop instance -
_autodiscover_usb
()[source]¶ Attempt to find the serial adapter for the hottop.
This will loop over the USB serial interfaces looking for a connection that appears to match the naming convention of the Hottop roaster.
Returns: string
-
_callback
(data)[source]¶ Processor callback to clean-up stream data.
This function provides a hook into the output stream of data from the controller processing thread. Hottop readings are saved into a local class variable for later saving. If the user has defined a callback, it will be called within this private function.
Parameters: data (dict) – Information from the controller process Returns: None
-
_derive_charge
(config)[source]¶ Use a temperature window to identify the roast charge.
The charge will manifest as a sudden downward trend on the temperature. Once found, we save it and avoid overwriting. The charge is needed in order to derive the turning point.
Parameters: config (dict) – Current snapshot of the configuration Returns: None
-
_derive_turning_point
(config)[source]¶ Use a temperature window to identify the roast turning point.
Turning point relies on the charge being set first. We use the rolling 5-point window to measure slope. If we show a positive trend after the charge, then the temperature has begun to turn.
Parameters: config (dict) – Current snapshot of the configuration Returns: None
-
add_roast_event
(event)[source]¶ Add an event to the roast log.
This method should be used for registering events that may be worth tracking like first crack, second crack and the dropping of coffee. Similar to the standard reading output from the roaster, manually created events will include the current configuration reading, time and metadata passed in.
Parameters: event (dict) – Details describing what happened Returns: dict
-
connect
(interface=None)[source]¶ Connect to the USB for the hottop.
Attempt to discover the USB port used for the Hottop and then form a connection using the serial library.
Returns: bool Raises: SerialConnectionError –
-
drop
()[source]¶ Preset call to drop coffee from the roaster via thread signal.
This will set the following configuration on the roaster: - drum_motor = 0 - heater = 0 - solenoid = 1 - cooling_motor = 1 - main_fan = 10
In order to power-off the roaster after dropping coffee, it’s best to use the shutdown method. It’s assumed that cooling will occur for 5-10 minutes before shutting down.
Returns: None
-
end
()[source]¶ End the roaster control process via thread signal.
This simply sends an exit signal to the thread, and shuts it down. In order to stop monitoring, call the set_monitor method with false.
Returns: None
-
set_cooling_motor
(cooling_motor)[source]¶ Set the cooling motor config.
Parameters: cooling_motor (bool) – Value to set the cooling motor Returns: None Raises: InvalidInput
-
set_drum_motor
(drum_motor)[source]¶ Set the drum motor config.
Parameters: drum_motor (bool) – Value to set the drum motor Returns: None Raises: InvalidInput
-
set_fan
(fan)[source]¶ Set the fan config.
Parameters: fan (int [0-10]) – Value to set the fan Returns: None Raises: InvalidInput
-
set_heater
(heater)[source]¶ Set the heater config.
Parameters: heater (int [0-100]) – Value to set the heater Returns: None Raises: InvalidInput
-
set_interval
(interval)[source]¶ Set the polling interval for the process thread.
Parameters: interval (int or float) – How often to poll the Hottop Returns: None Raises: InvalidInput
-
set_main_fan
(main_fan)[source]¶ Set the main fan config.
Parameters: main_fan (int [0-10]) – Value to set the main fan Returns: None Raises: InvalidInput
-
set_monitor
(monitor)[source]¶ Set the monitor config.
This module assumes that users will connect to the roaster and get reading information _before_ they want to begin collecting roast details. This method is critical to enabling the collection of roast information and ensuring it gets saved in memory.
Parameters: monitor (bool) – Value to set the monitor Returns: None Raises: InvalidInput
-
set_roast_properties
(settings)[source]¶ Set the properties of the roast.
Parameters: settings (dict) – General settings for the roast setup Returns: None Raises: InvalidInput
-
set_simulate
(status)[source]¶ Set the simulation status.
Parameters: status (bool) – Value to set the simulation Returns: None Raises: InvalidInput
-
set_solenoid
(solenoid)[source]¶ Set the solenoid config.
Parameters: solenoid (bool) – Value to set the solenoid Returns: None Raises: InvalidInput
-
start
(func=None)[source]¶ Start the roaster control process.
This function will kick off the processing thread for the Hottop and register any user-defined callback function. By default, it will not begin collecting any reading information or saving it. In order to do that users, must issue the monitor/record bit via set_monitor.
Parameters: func (function) – Callback function for Hottop stream data Returns: None
-
Control Process¶
Due to the nature of continuously needing to poll the serial interface, a thread was required to handle interactions with the serial interface. It’s possible to use the multiprocessing module to handle this work, but when using this library inside of web server technology, multiprocessing often causes issues. Vanilla threads were used here to avoid interaction problems with co-routine or eventlet-based libraries.
-
class
pyhottop.pyhottop.
ControlProcess
(conn, config, q, logger, callback=None)[source]¶ Primary processor to communicate with the hottop directly.
Parameters: - conn (Serial instance) – Established serial connection to the Hottop
- config (dict) – Initial configurations settings
- q (Queue instance) – Shared queue to interact with the user interface
- logger (Logging instance) – Shared logger to keep continuity
- callback (function) – Optional callback function to stream results
Returns: ControlProces instance
-
_generate_config
()[source]¶ Generate a configuration that can be sent to the Hottop roaster.
Configuration settings need to be represented inside of a byte array that is then written to the serial interface. Much of the configuration is static, but control settings are also included and pulled from the shared dictionary.
Returns: Byte array of the prepared configuration.
-
_read_settings
(retry=True)[source]¶ Read the information from the Hottop.
Read the settings from the serial interface and convert them into a human-readable format that can be shared back to the end-user. Reading from the serial interface will occasionally produce strange results or blank reads, so a retry process has been built into the function as a recursive check.
Returns: dict
-
_send_config
()[source]¶ Send configuration data to the hottop.
Returns: bool Raises: Generic exceptions if an error is identified.
-
_valid_config
(settings)[source]¶ Scan through the returned settings to ensure they appear sane.
There are time when the returned buffer has the proper information, but the reading is inaccurate. When this happens, temperatures will swing or system values will be set to improper values.
Parameters: settings (dict) – Configuration derived from the buffer Returns: bool
-
_validate_checksum
(buffer)[source]¶ Validate the buffer response against the checksum.
When reading the serial interface, data will come back in a raw format with an included checksum process.
Returns: bool
-
_wake_up
()[source]¶ Wake the machine up to avoid race conditions.
When first interacting with the Hottop, the machine may not wake up right away which can put our reader into a death loop. This wake up routine ensures we prime the roaster with some data before starting our main loops to read/write data.
Returns: None
-
run
()[source]¶ Run the core loop of reading and writing configurations.
This is where all the roaster magic occurs. On the initial run, we prime the roaster with some data to wake it up. Once awoke, we check our shared queue to identify if the user has passed any updated configuration. Once checked, start to read and write to the Hottop roaster as long as the exit signal has not been set. All steps are repeated after waiting for a specific time interval.
There are also specialized routines built into this function that are controlled via events. These events are unique to the roasting process and pre-configure the system with a configuration, so the user doesn’t need to do it themselves.
Returns: None
Exceptions¶
Changelog¶
Running list of changes to the library.
2017-03-15¶
- Bugfix: Capture error when validating byte sequence
2017-03-07¶
- Change: Removed non-python3 dict method
- Bugfix: Error in valid config checking
2017-02-24¶
- Change: Added logic to add event code to find a valid configuration before saving
2017-02-10¶
- Change: Added logic to turning point logic to avoid setting too soon
2017-12-20¶
- Feature: Added a mock service to simulate a roast without being connected to a machine
2017-12-10¶
- Bugfix: Removed the reset on start as it clears any properties set by the user
2017-12-06¶
- Change: Keep the drum on by default to avoid any stalls
2017-12-03¶
- Change: Wrap the buffer read and pull from cache if it continues to fail
- Change: Adjusted lower bound temperature to 50
- Feature: Reset all the roast settings when starting a roast
2017-12-02¶
- Bugfix: Called the proper logging object on buffer measurement
- Change: Added raw buffer responses to the event log
- Feature: Added a validate routine to the buffer read to account for inaccurate responses from the roaster
- Feature: Automatically derive charge and turning point events based on temperature data
2017-12-01¶
- Bugfix: Turned drum motor on when doing a cool-down to push beans out
2017-11-29¶
- Bugfix: Replaced existing extenal_temp with environment_temp
- Bugfix: Fixed issue with buffer retry loop where it was not being called
- Change: Adjusted default interval to 1 second to avoid buffer issues
- Change: Toggle serial connection if having trouble reading buffer
2017-11-28¶
- Change: Adjusted duration to be of format MM:SS instead of total seconds
- Change: Return roast state when toggling monitoring
2017-11-24¶
- Feature: several new methods for getting additional roast details
- Change: Refactored code related to tracking roast properties and timing
- Change: Updated documentation within the code
- Bugfix: when running with python3 due to queue library
License¶
Copyright 2017 Split Key Coffee
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.