Getting Artisan to talk to Arduino - Page 4
-
- Posts: 69
- Joined: 4 years ago
thanks robhh for the knowledge sharing and insight!robhh wrote:Good to hear you got it working
You can increase the batch size as long as the beans move and change place... Specially at the beginning of the process when density is at its highest.
Yes, you will have to calibrate the temp sensor to get good results. Otherwise your system will perform different from others and the results can not be compared. I would go for a thermocouple sensor because they are linear by nature, and therefore easier to calibrate across the whole range.
I added an alog file which can be used as background profile (20-04-19_0058.alog). You set it as background under ''Roast', 'Background'. I made it with the 'Tools'. 'Designer' tool in Artisan. It is straight forward to use and a nice tool to set up a profile. It might be a bit darker than what you prefer, just try a small batch.
-
- Posts: 3
- Joined: 4 years ago
I'm coming late to this thread, but I got Arduino and Artisan to talk back and forth over wifi using the ModbusTCP protocol in Arduino. I figured I would share the parts that took me a while to figure out
To get it to work, I had to setup the Arduino unit as the ModbusTCP Server (slave), write to holding registers, and then let Artisan can poll the arduino/server to read and write to those registers.
The WiFiModbusServerLED example (under ArduinoModbus>TCP) shows how to start the wifi and modbus server, poll for requests (from artisan), and write to a modbus coil: https://github.com/arduino-libraries/Ar ... verLED.ino
I had to use Modbus registers to pass 16-bit temperatures instead of 1-bit coils -- the code looks like this:
When it's time to grab the thermocouple/bean temps and write them, it looks like this:
Whenver artisan calls this TCP modbus server, it gets returned whatever the most-recent value of the register.
Configuring Artisan under 'devices' looks like this (the IP address is whatever your network assigns to the arduino.. you can check your wifi router to get this, write it out to serial in arduino ide, or snap and add a display like I did). Each of the registers from the arduino code get added:
I added extra signals as LCDs in Artisan:
And, I added a Slider which has a Modbus Command mapped to it (it writes the slider value to the Arduino, then the normal artisan dashboard picks up the change a few seconds later):
The extra LCDs and Slider show up like this:
I'm using an arduino Nano 33 IOT (which has a little NINA-w102 wifi/bluetooth module), a MAX31855 thermocouple board, and a little OLED display squished into an altoids-like tin.
I roast on an RK-drum on my BBQ and wanted to measure bean temp, so I needed wireless something that could attach to the rotisserie spit handle with a thermocouple running to the drum (and spin round and round and round). I run artisan on a computer inside the house, and I use my android phone to remote-desktop in and view Artisan while I roast outside.
To get it to work, I had to setup the Arduino unit as the ModbusTCP Server (slave), write to holding registers, and then let Artisan can poll the arduino/server to read and write to those registers.
The WiFiModbusServerLED example (under ArduinoModbus>TCP) shows how to start the wifi and modbus server, poll for requests (from artisan), and write to a modbus coil: https://github.com/arduino-libraries/Ar ... verLED.ino
I had to use Modbus registers to pass 16-bit temperatures instead of 1-bit coils -- the code looks like this:
modbusTCPServer.configureHoldingRegisters(0x00, 4); //start at 0x00 and create 4 registers
modbusTCPServer.holdingRegisterWrite(0x00,0); //BT at 0x00, set to 0 by default
modbusTCPServer.holdingRegisterWrite(0x01,0); //Air-fan config at 0x01, set to 0 by default (sent from Artisan, proof of concept)
modbusTCPServer.holdingRegisterWrite(0x02,0); //DrumSpeed at 0x02, set to 0 by default (measured by arduino nano gyroscope)
modbusTCPServer.holdingRegisterWrite(0x03,0); //ArduinoTemp at 0x03, set to 0 by default (thermocouple boards temp)
int32_t arduinoTemperature = MAX31855.readAmbient()/100; // retrieve MAX31855 die ambient temperature
int32_t probeTemperature = MAX31855.readProbe()/100; // retrieve thermocouple probe temp
//check for thermocouple faults/errors
uint8_t faultCode = MAX31855.fault(); // retrieve any error codes
if ( faultCode ) // Display error code if present
{
Serial.print("Fault code ");
Serial.print(faultCode);
Serial.println(" returned.");
modbusTCPServer.holdingRegisterWrite(0x00,0);
modbusTCPServer.poll();
}
else
{
modbusTCPServer.holdingRegisterWrite(0x00, (uint16_t)(probeTemperature)); //update modbus register with thermocouple temp
modbusTCPServer.holdingRegisterWrite(0x03, (uint16_t)(arduinoTemperature)); //update modbus register with MAX31855 temp
}
Configuring Artisan under 'devices' looks like this (the IP address is whatever your network assigns to the arduino.. you can check your wifi router to get this, write it out to serial in arduino ide, or snap and add a display like I did). Each of the registers from the arduino code get added:
I added extra signals as LCDs in Artisan:
And, I added a Slider which has a Modbus Command mapped to it (it writes the slider value to the Arduino, then the normal artisan dashboard picks up the change a few seconds later):
The extra LCDs and Slider show up like this:
I'm using an arduino Nano 33 IOT (which has a little NINA-w102 wifi/bluetooth module), a MAX31855 thermocouple board, and a little OLED display squished into an altoids-like tin.
I roast on an RK-drum on my BBQ and wanted to measure bean temp, so I needed wireless something that could attach to the rotisserie spit handle with a thermocouple running to the drum (and spin round and round and round). I run artisan on a computer inside the house, and I use my android phone to remote-desktop in and view Artisan while I roast outside.
-
- Posts: 69
- Joined: 4 years ago
Spambojambo, thanks! That's awesome sharing.
Can you kindly share your codes as well?
One question, why did you decide on ModbusTCP protocol? Is it because you wanted to use the TCP/IP / Wireless network to exchange the information?
Oh.. and do show us the photo of the altoids tin
Can you kindly share your codes as well?
One question, why did you decide on ModbusTCP protocol? Is it because you wanted to use the TCP/IP / Wireless network to exchange the information?
Oh.. and do show us the photo of the altoids tin
-
- Posts: 3
- Joined: 4 years ago
Sure - I posted the code here: https://github.com/spambojambo/ArduinoToArtisanWiFi
Fair warning: there is a bunch of uninteresting code in there to deal with the OLED (posting info and status to the OLED, avoiding screen burning, turning the display off when the drum starts spinning) and attempts to smooth out the drumspeed signal.
Why ModbusTCP? I started down the bluetooth route and discovered my ardiono nano sense ble uses Bluetooth 4.0 (Low Energy). It turns out that 4.0 is low energy because it's not intended to maintain a steady, serial stream of data -- it hosts data other devices poll to get. This wasn't ideal and looked like more code on the arduino and on the receiving PC than I wanted to figure out.
While waiting on a new arduino nano that uses the older bluetooth (Nano 33 IoT), I learned more about Modbus since many devices use that with artisan. I discovered ModbusTCP as an option, realized my new Nano has wifi, and decided to try that route. I realized I could run artisan on a virtual machine on a computer in the house and then connect to it through my Android phone -- much simpler than putting a laptop in harm's way outside next to the smokey bbq roaster.
This is what the phone screen looks like (screen recorded through a windows app -- I didn't have a phone to take a picture of this phone):
Behold the mighty altoids tin....stuck to a USB battery pack... stuck to a rotisserie handle:
When it's time to roast, I screw the handle onto the spi trod where the previously rubberbanded thermometer lived (and spun and spun):
The design has iterated a bit (e.g., that thermocouple wire did not like that 90 degree bend + 500 degrees), and it's not the strongest wifi signal, but it's a fun project.
Fair warning: there is a bunch of uninteresting code in there to deal with the OLED (posting info and status to the OLED, avoiding screen burning, turning the display off when the drum starts spinning) and attempts to smooth out the drumspeed signal.
Why ModbusTCP? I started down the bluetooth route and discovered my ardiono nano sense ble uses Bluetooth 4.0 (Low Energy). It turns out that 4.0 is low energy because it's not intended to maintain a steady, serial stream of data -- it hosts data other devices poll to get. This wasn't ideal and looked like more code on the arduino and on the receiving PC than I wanted to figure out.
While waiting on a new arduino nano that uses the older bluetooth (Nano 33 IoT), I learned more about Modbus since many devices use that with artisan. I discovered ModbusTCP as an option, realized my new Nano has wifi, and decided to try that route. I realized I could run artisan on a virtual machine on a computer in the house and then connect to it through my Android phone -- much simpler than putting a laptop in harm's way outside next to the smokey bbq roaster.
This is what the phone screen looks like (screen recorded through a windows app -- I didn't have a phone to take a picture of this phone):
Behold the mighty altoids tin....stuck to a USB battery pack... stuck to a rotisserie handle:
When it's time to roast, I screw the handle onto the spi trod where the previously rubberbanded thermometer lived (and spun and spun):
The design has iterated a bit (e.g., that thermocouple wire did not like that 90 degree bend + 500 degrees), and it's not the strongest wifi signal, but it's a fun project.
-
- Posts: 69
- Joined: 4 years ago
woohoo!!! Spambojambo thanks for sharing your project, codes and especially the built!!!
-
- Posts: 69
- Joined: 4 years ago
Hi..
recently, I found that k-type thermocouple connected to max6675 via Arduino to my raspberry PI seems to have more noise (in the readings), with my raspberry PI powered by a wall plug. a USB isolator doesn't eliminate the noise.
does anyone have similar experience?
i found that running raspberry PI off battery pack eliminates the problem, but i am pretty sure I didn't have this problem previously when my raspberry PI was connected to a wall plug (power supply). And I am using the same wall plug as before.
Additional Notes:
when I used Phidgets connected to the Raspberry PI (with the same k-type thermocouple), I do not have any noise (in the readings) issue.
hmmmmm....
recently, I found that k-type thermocouple connected to max6675 via Arduino to my raspberry PI seems to have more noise (in the readings), with my raspberry PI powered by a wall plug. a USB isolator doesn't eliminate the noise.
does anyone have similar experience?
i found that running raspberry PI off battery pack eliminates the problem, but i am pretty sure I didn't have this problem previously when my raspberry PI was connected to a wall plug (power supply). And I am using the same wall plug as before.
Additional Notes:
when I used Phidgets connected to the Raspberry PI (with the same k-type thermocouple), I do not have any noise (in the readings) issue.
hmmmmm....
-
- Posts: 69
- Joined: 4 years ago
-
- Posts: 8
- Joined: 5 years ago
Hi guys, has anyone had success communicating with Artisan using the TC4 protocol, but not actually using a TC4?
I'm running into the same issue Smplif8 had - I can only communicate with Artisan if I "hard reset" the Arduino by pressing the physical reset button on the Arduino. After the hard reset Artisan initializes the Arduino connection and the temperatures are displayed perfectly. I can also send the commands (CHAN, READ, etc) via serial monitor and get the correct response from the Arduino.
I don't want to reset the Arduino every time I connect, so this has me stumped for the moment.
My setup:
Arduino Mega 2560
Artisan 2.4.2, connected with USB
4x MAX 6675 thermocouple boards
I'm running into the same issue Smplif8 had - I can only communicate with Artisan if I "hard reset" the Arduino by pressing the physical reset button on the Arduino. After the hard reset Artisan initializes the Arduino connection and the temperatures are displayed perfectly. I can also send the commands (CHAN, READ, etc) via serial monitor and get the correct response from the Arduino.
I don't want to reset the Arduino every time I connect, so this has me stumped for the moment.
My setup:
Arduino Mega 2560
Artisan 2.4.2, connected with USB
4x MAX 6675 thermocouple boards
//Arduino --> Artisan Roaster Scope Communication Test
// TC4 Communication Code adapted from FilePhil https://github.com/FilePhil/TC4-Emulator/blob/master/TC4-Emulator.ino
// TC4 Communication code also adapted from GreenCardigan https://github.com/greencardigan/TC4-shield/blob/master/applications/Artisan/aArtisan/tags/REL-310/libraries/cmndproc/cmndproc.cpp
#include "max6675.h"
#include <SPI.h>
//TC4 Emulator.ino
bool unit_F = false; //true = °F - false = °C
//Set Chip Select Pins
const int tc1CS = 24;
const int tc2CS = 28;
const int tc3CS = 30;
const int tc4CS = 32;
const int thermoDO = 26;
const int thermoCLK = 22;
const long BAUD = 115200;
unsigned long currentMillis;
unsigned long startMillis;
const unsigned long measureInterval = 250; //Max6675 thermocouple boards require about 250ms to stabilize between measurements otherwise they return the previous measurement.
MAX6675 thermocouple1(thermoCLK, tc1CS, thermoDO);
MAX6675 thermocouple2(thermoCLK, tc2CS, thermoDO);
MAX6675 thermocouple3(thermoCLK, tc3CS, thermoDO);
MAX6675 thermocouple4(thermoCLK, tc4CS, thermoDO);
double temp1;
double temp2;
double temp3;
double temp4;
String msg;
// These variables are used for serial data input and parsing. Taken from example 5 of Robin2's thread "Serial Input Basics - updated" https://forum.arduino.cc/index.php?topic=396450.0
#define MAX_CMND_LEN 40 // max overall characters in a command line
#define MAX_RESULT_LEN 9 // max length of result string sent back to caller
char cmndstr[MAX_CMND_LEN + 1];
char result[MAX_RESULT_LEN + 1];
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
boolean newData = false;
// end serial variables
double readThermoCouple(MAX6675 &thermo)
{
double tempT;
if (unit_F) {
tempT = thermo.readFarenheit();
}
else {
tempT = thermo.readCelsius();
}
if (isnan(tempT)) {
return 0;
}
else {
return tempT;
}
}
void parseData() { // split the data into its parts
// This will require lots of changes for Artisan. Artisan lines to parse: "CHAN;ijkl" "UNITS;u" "READ"
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(cmndstr, ";"); // get the first part - the string, from cmndst
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
//Serial.println(messageFromPC);
if (strcmp(messageFromPC, "READ") == 0) {
Command_READ();
}
else if (strcmp(messageFromPC, "CHAN") == 0) {
Serial.println("#OK");
}
else if (strcmp(messageFromPC, "UNITS") == 0) {
strtokIndx = strtok(NULL, ";");
//Serial.println(strtokIndx);
if (strcmp(strtokIndx, "F") == 0) {
unit_F = true;
Serial.println("#OK Fahrenheit");
}
else if (strcmp(strtokIndx, "C") == 0) {
unit_F = false;
Serial.println("#OK Celsius");
}
}
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx); // convert this part to a float
}
void recvWithEndMarker() { //changed example 5 to eliminate start markers - Artisan uses newlines only, no start markers.
//removed "recvInProgress" because it's not used in recvwithEndMarker - will this cause issues elsewhere?
static byte ndx = 0;
char endMarker = '\n';
char rc;
while (Serial.available() > 0) {
rc = Serial.read();
//check for newline, buffer overflow
uint8_t len = strlen(cmndstr);
if ((rc == '\n') || (len == MAX_CMND_LEN)) { //if the character read is either newline or the string has reached max length, send result back to be parsed.
strncpy(result, cmndstr, MAX_RESULT_LEN); //copy cmndstr to "result"
result[MAX_RESULT_LEN] = '\0'; //adds a null character for "safety"
parseData(); //send the "result" off for parsing
cmndstr[0] = '\0'; //empty the buffer
return result; //not sure if I need to return this, hopefully loop will exit in ParseData and this code won't be used?
}//end if
else if ( rc != '\r') { //skip carriage returns. If not a CR, add character to array.
cmndstr[len] = toupper(rc); //converts char to upper case, appends it to cmndstr
cmndstr[len + 1] = '\0'; //adds NULL to next spot in array
} //end else
} //end while
return NULL;
// receivedChars[ndx] = rc;
// ndx++;
// if (ndx >= numChars) {
// ndx = numChars - 1;
// }
// }
// else {
// receivedChars[ndx] = '\0'; // terminate the string
// ndx = 0;
// newData = true;
// }
// }
}
//============
//Send Data
void Command_READ() {
Serial.print("0.00,");
Serial.print(temp1);
Serial.print(",");
Serial.print(temp2);
Serial.print(",");
Serial.print(temp3);
Serial.print(",");
Serial.println(temp4);
}
//Parsing Serial Commands
void handleSerialCommand() {
recvWithEndMarker();
}
void setup() {
// SPI.begin();
Serial.begin(BAUD);
}
void loop() {
currentMillis = millis();
if (currentMillis - startMillis >= measureInterval) //Test whether enough time has passed to poll Max6675 thermocouples
{
temp1 = readThermoCouple(thermocouple1);
temp2 = readThermoCouple(thermocouple2);
temp3 = readThermoCouple(thermocouple3);
temp4 = readThermoCouple(thermocouple4);
startMillis = currentMillis; //add current time to counter to reset interval.
}
handleSerialCommand();
}
-
- Posts: 15
- Joined: 6 years ago
This is a simple Arduino sketch allowing tracking of two temperatures simulating a TC4 response to Artisan READ commands. It is written for two MAX6675 breakout boards with attached thermocouples with an Arduino Nano.You should be able to extend it to four breakout boards.
Here is how you connect it in artisan:
https://www.dropbox.com/s/j5t1gvsqfd645 ... s.jpg?dl=0
Have fun!
// Kaffee-Netz Thread: https://www.kaffee-netz.de/threads/roester-eigenbau-wie-artisan-verwenden.119706
#include <max6675.h>
int pinGND = 2;
int pinVCC = 3;
int pinSCK = 4;
int pinCS = 5;
int pinSO = 6;
int pinGND2 = 8;
int pinVCC2 = 9;
int pinSCK2 = 10;
int pinCS2 = 11;
int pinSO2 = 12;
MAX6675 thermocouple(pinSCK, pinCS, pinSO);
MAX6675 thermocouple2(pinSCK2, pinCS2, pinSO2);
void setup() {
Serial.begin(19200);
pinMode(pinVCC, OUTPUT);
pinMode(pinGND, OUTPUT);
pinMode(pinVCC2, OUTPUT);
pinMode(pinGND2, OUTPUT);
digitalWrite(pinVCC, HIGH);
digitalWrite(pinGND, LOW);
digitalWrite(pinVCC2, HIGH);
digitalWrite(pinGND2, LOW);
delay(500);
}
void loop() {
if(Serial.available()) {
String command = Serial.readStringUntil('\n');
if (command.startsWith("CHAN") || command.startsWith("UNIT") || command.startsWith("FILT")) {
Serial.println("#");
Serial.flush();
return;
}
else if(command == "READ") {
double cet = thermocouple.readCelsius();
double cbt = thermocouple2.readCelsius();
Serial.println("0,"+ String(cet) +"," + String(cbt));
Serial.flush();
}
}
delay(100);
}
https://www.dropbox.com/s/j5t1gvsqfd645 ... s.jpg?dl=0
Have fun!
-
- Posts: 15
- Joined: 6 years ago
And this is an Arduino sketch which allows tracking of one temperature (Bean temperature) and control of a heating lamp via a cheap AC Dimmer module (RobotDyn AC Dimmer Module). The dimmer value is send by Artisan using a serial command. With this you can use the Artisan Software PID to control your AC heating element/lamp while reading bean temperature. This could be extended to more thermocouples if needed. Maybe someone finds this sketch useful. It works well with my halogen lamp air fryer, I posted the mod in this subforum.
/* Dieser Sketch erlaubt das gleichzeitige Auslesen der Thermocouple-Temperatur sowie das Einstellen des Dimmerwertes des RobotDyn Moduls
in der Klarstein Vitar durch die Röstersoftware Artisan. Der Sketch basiert auf einem Sketch von User coffeewhiskey und simuliert Artisan
ein TC4 Board. Einstellungen in Artisan siehe Datei.
*/
// siehe auch Kaffee-Netz Thread: https://www.kaffee-netz.de/threads/roester-eigenbau-wie-artisan-verwenden.119706
#include <max6675.h>
#include <RBDdimmer.h>
//#define USE_SERIAL SerialUSB //Serial for boards whith USB serial port
#define USE_SERIAL Serial
#define outputPin 3
#define zerocross 2 // for boards with CHANGEABLE input pins
//dimmerLamp dimmer(outputPin, zerocross); //initialase port for dimmer for ESP8266, ESP32, Arduino due boards
dimmerLamp dimmer(outputPin); //initialase port for dimmer for MEGA, Leonardo, UNO, Arduino M0, Arduino Zero
int outVal = 0;
int pinGND = 9;
int pinVCC = 8;
int pinSCK = 7;
int pinCS = 6;
int pinSO = 5;
int pinDIMMERVCC = 11;
MAX6675 thermocouple(pinSCK, pinCS, pinSO);
void setup() {
USE_SERIAL.begin(9600);
dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE)
pinMode(pinVCC, OUTPUT);
pinMode(pinGND, OUTPUT);
pinMode(pinDIMMERVCC, OUTPUT);
digitalWrite(pinVCC, HIGH);
digitalWrite(pinGND, LOW);
digitalWrite(pinDIMMERVCC, HIGH);
delay(500);
}
void printSpace(int val)
{
if ((val / 100) == 0) USE_SERIAL.print(" ");
if ((val / 10) == 0) USE_SERIAL.print(" ");
}
void loop() {
if(USE_SERIAL.available()) {
String command = USE_SERIAL.readStringUntil('\n');
if (command.startsWith("CHAN") || command.startsWith("UNIT") || command.startsWith("FILT")) {
USE_SERIAL.println("#");
USE_SERIAL.flush();
return;
}
else if (command == "READ") {
double cbt = thermocouple.readCelsius();
printSpace(dimmer.getPower());
USE_SERIAL.print(dimmer.getPower()); //writes Dimmer Power-Value as TC4 Ambient Temperature
USE_SERIAL.println(",0," + String(cbt)); //write Dimmer value plus Bean Temperature for Artisan
USE_SERIAL.flush();
return;
}
else {
int preVal = outVal;
int buf = command.toInt(); //writes string to integer for value of dimmer
USE_SERIAL.flush();
if (buf >= 10) {{outVal = buf;}
digitalWrite(pinDIMMERVCC, HIGH);
delay(100);
dimmer.setPower(outVal); // setPower(0-100%);
delay(100);
}
else {
{outVal = buf;}
dimmer.setPower(outVal);
delay(100);
digitalWrite(pinDIMMERVCC, LOW);
delay(100);
}
}
}
delay(100);
}