Showing posts with label Hackaday. Show all posts
Showing posts with label Hackaday. Show all posts

Sunday, March 8, 2020

Eggbot; filling in some missing information

I guess it is that time of year, people are building Eggbots. I built one, and needed to learn a bunch. Some of the information was vague, or not connected to the original design, or anyone elses design.



I started with a Russian frame that I liked on Thingiverse. This was mostly lacking any real documentation. Elliot from Hackaday has a page with lots of information, including how to make a CNC shield move a servo, and how to get G code out of inkscape.

The Russian frame had a bunch of options for the pen holder. I chose the simple one, and am having success using that with a sharpie fine point. Being an American, it was easier to get 1/4-20 5/16-18 threaded rod than 6mm, so that is what I used. I cut 3 1ft pieces. For the rest of the bolts and screws, I used metric (m4 and m3 mostly), since the motors assume metric.

One tool that I will call essential is the taper cutter. Cutting threaded rod is easy, but the threads are suddenly all mucked up. Either the taper goes away, and threading on a nut has to be aligned perfectly, or the threads are bunged up so badly nothing will thread on anymore. This tool will make the trouble disappear, in about 2 seconds!. Sure you could file a taper in using a file, or grinder, but for less than $5, why?

The Russian  frame also assumed some 16mm bearings, that I didn't order. Another similar frame used skateboard bearings in a holder with the same bolt pattern as the stepper motors. I just reprinted the left frame from the Russian frame and two of these bearing holders for the end.

I couldn't find O rings that would hang onto the egg good. One end or the other would slip, and the drawing wasn't real good. I found some #17 bath diverter washers that seemed to be a better shape, and fit the end caps well. They still slipped.


I've started playing with some table foot rubber pieces, like on the bottom of a crutch. They are more grippy. I'll update when I know more.

Some page, maybe another one on Thingiverse suggested I use a CNC V4 Shield. A great design, with the Arduino Nano right on the board with the stepper drivers. The trouble is, the common Chinese clone makers messed up the board. A couple others have figured out the changes needed, and published the wiring changes to make this board work.

What I have seen though, the inkscape G code output doesn't quite make the pen up/down command that the GRBL expects. The delays are about 100 times longer than they say (seconds instead of milliseconds) as well  (Update, in the save dialog, it lets you set delay to 0, that seems to work). Long delays with the pen over at an angle almost always messes up the continuation of the lines. I end up hand editing the G code output. I should probably build a python or perl script to fix the output, or just figure out what unicorn changes are needed.

Changing the GRBL source code, it is important to remember you moved the code to the Arduino directory, so change it there, not where you expanded the code. If the motors are going backwards, reverse the plugs on the CNC V4 board.

For test eggs, I have been using the 18 for a dollar plastic eggs that I can get at the Dollar Tree. I like hard boiled eggs, but I didn't think I'd eat 24-36 in the next couple of weeks. I should look into dry erase markers to see if I can erase the test eggs.




Monday, April 1, 2019

Something Didn't Work

I am writing about another input node. I have a water softener in my house, it goes with the well, since the water is hard, and there is a bit of iron. Every month or so I need to put about 200lbs of salt in the tank. The tank is large and opaque, so I don't actually end up opening the tank, to check on it as often as I should.

I read a story on hackaday about someone who built a water softener monitor. I thought I could do something similar. I thought I could use a simple HC-SR04 ultrasonic module to measure how far down the salt was from the sensor. Connecting one of these sonar modules is dirt simple, and then enclosing the sensor in a 3D printed box would allow measuring to the salt.




Everything seemed to work, the first month. The device is in the tank, I have a USB charger cable going out the the slot in the tank where the brine flushes, so no real modifications. The sensor box is zip tied to the larger tube in the tank. It measures a distance, that seems consistent even though the surface isn't uniform.

To get familiar with the setup, I had the node email me the measurement every night about 11PM. This seemed to go great. A couple measurements were way out of spec, but mostly it was going down a few centimeters every couple days. The email reminded me to check on the tank a couple times a week. After it got down to a level where I thought I should fill it, I changed the code to only email me when it got more than a few centimeters of that level.

Here is the code for the node:

/** 
 *  Sensor that measures salt level in water softener, and sends result over MQTT (node-red)
 *    Sonar distance measured at regular intervals sent over MQTT.
 *
 * The board selected in the IDE is WeMos D1 R2 & Mini. Standard everything 
 * 
 * 
 * TGB 2/20/19
 */

 
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <PubSubClientTools.h>

// Update these with values suitable for your network.

const char* ssid = "raspi-webgui";
const char* password = "XXXXXXXX";
const char* MQTT_SERVER = "10.3.141.1";

#define echoPin 12 // Echo Pin
#define trigPin 13 // Trigger Pin

WiFiClient espClient;
PubSubClient client(MQTT_SERVER, 1883, espClient);
PubSubClientTools mqtt(client);

long lastMsg = 0;
char msg[50];
int value = 0;
String s = "";

int maximumRange = 200; // Maximum range needed
int minimumRange = 0; // Minimum range needed
long duration, distance; // Duration used to calculate distance

void setup() 
{
  // put your setup code here, to run once:
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();


  // Connect to MQTT
  Serial.print(s+"Connecting to MQTT: "+MQTT_SERVER+" ... ");
  if (client.connect("ESP8266Client")) {
    Serial.println("connected");
  } else {
    Serial.println(s+"failed, rc="+client.state());
  }
  
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  setup_wifi();
}

void setup_wifi() 
{
  // Connect to WiFi
  Serial.print(s+"Connecting to WiFi: "+ssid+" ");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(s+" connected with IP: "+WiFi.localIP());
   digitalWrite(BUILTIN_LED, HIGH); // turn LED off

  // Connect to MQTT
  Serial.print(s+"Connecting to MQTT: "+MQTT_SERVER+" ... ");
  if (client.connect("SaltReader")) {
    Serial.println("connected");
  } else {
    Serial.println(s+"failed, rc="+client.state());
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("SaltReader")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic","saltReader Restart");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      // Wait 5 seconds before retrying
    }
  }
}


//////////////////////////////////////////////////////////////////////////
////////// Sonar Sample //////////////////////////////////////////////////
long sample() 
{
/* The following trigPin/echoPin cycle is used to determine the
 distance of the nearest object by bouncing soundwaves off of it. */ 
 digitalWrite(trigPin, LOW); 
 delayMicroseconds(2); 

 digitalWrite(trigPin, HIGH);
 delayMicroseconds(10); 
 
 digitalWrite(trigPin, LOW);
 duration = pulseIn(echoPin, HIGH);
 
 //Calculate the distance (in cm) based on the speed of sound.
 distance = duration/58.2;
 
 if (distance >= maximumRange || distance <= minimumRange)
 {
   /* Send a negative number to computer and Turn LED ON 
   to indicate "out of range" */
   Serial.println("-1");
 }
 else
 {
   /* Send the distance to the computer using Serial protocol, and
   turn LED OFF to indicate successful reading. */
   Serial.println(distance);
 }
 return distance;
}


void publisher() {
  long now = millis();
  if (now - lastMsg > 30000) {  // 30 seconds
    lastMsg = now;
    long distance=sample();
    ++value;
    
    snprintf (msg, 75, "%ld", distance);
    Serial.print("Publish message: ");
    Serial.println(msg);
    mqtt.publish("salt/level", msg);
  }
}

void loop() 
{
  if (!client.connected()) {
    reconnect();
  }
  
  client.loop();
  publisher();
}


This node sends a MQTT message on the "salt/level" topic with the measured distance as the payload. The time in the loop is 30 seconds, so twice a minute this will publish the message. That is all it needs to do.

The flow in Node Red is:



The receiver listens for messages on the "salt/level" topic. The output of that node goes to a debug node, an email formatter function and two dashboard items, slider and text. The email formatter also goes to an email node and another debug node.

The function that formats the email is:


context.saltLevel = 0;

if (msg.topic === 'salt/level') {
  context.saltLevel = msg.payload;
}


if ((context.global.minute == 32) && 
    (context.global.hour == 23) && 
    (context.saltLevel > 30))
{
    msg.payload = "Subject: Salt Level\n salt level "+context.saltLevel;
    return msg;  
}

msg.payload = context.global.hour+":"+context.global.minute 

The node context maintains the current salt level. I have another flow that outputs time, that I promise to show soon. The level is compared to a level of 30 centimeters, and if greater formats the email and sends the mail if the time is 23:32. Since the MQTT message is published every 30 seconds, I usually get 2 emails with the salt level. For me that is fine, it allows cross checking.

For the Dashboard, I have a "Salt Tank" group. This creates a tab just for the salt tank. The tab display is equally simple, and quick to determine the status:


Setting up email


If you want to use the email output node for alerts and such, and you use gmail, I suggest sending from some rarely used email address. Get a sub-gmail address to send from and use that as your send address. For the most part the node is easy to configure.


Gmail is very well protected. Google does a good job of filtering spam both from gmail senders and gmail receivers. To get around some of these protections, you need to turn down the safety of the gmail account that node-red will send from. In advanced settings set the sender to enable less secure access:



What is wrong


The dashboard is showing 38cm of salt. It should be sending email, and it does, every night. The trouble is, the salt tank is still about 1/3 full. Actually, the measurement is no longer consistent. A few times, this sent me email about the level being 109cm, and other times it sends email that the tank is at 77cm.

The basic fault might be that the box is in a high concentration of potentially moist salt dust. I am suspecting some form of corrosion, or possibly something conducting across something it shouldn't be.

Going back to the hackaday example, that person used a LIDAR instead of sonar. Lidar, being optical should allow the sensor box to be sealed, hopefully preventing humid salt from damaging the system.

For now the system mostly works. It is reminding me to look in the tub every few days, and we haven't run out of salt two months in a row.



Monday, July 8, 2013

I am doing a crummy job...

I am not keeping up with this, I am not documenting what works and what doesn't, and I just ignore all the readers.

Sorry

I'll be very humble in this post. Lots has changed. I bought a new desktop computer. This thing seems really good. It has two USB ports in the front of it, so I think that will help with things. Certainly the arduino plugs in nicely. I have the tablet, and a new phone. They are both Samsung Android devices. This is good, they both share nicely, so I can probably have some success.

Looking back, and trying to get things where I was, well, I am not having any luck.  I reinstalled the Arduino IDE, and looking back at my notes, I guess I missed some setups. The IDE won't let me talk to the board. The driver says successfully installed, but everytime I plug in, I get uh-ah sound from windows. This is Windows7, and I am following the instructions on the Arduino install page.

It seems the Arduino page isn't up to snuff. I went right to the FTDI page for the instructions on how to install the Windows 7 x64 device drivers (I don't think the 64 bit Intel drivers are included with Arduino at this time).  Yes, the instructions appear to make you do the same update driver twice, but yes you need to!

Even wanting to go back to where I was, I don't find any of my software, although it is probably on my old computer. How could you have duplicated, or improved my work? I need to do better than that. I am trying to be more open, and I hope you can understand.

This time the board is a Mega128, and the board is on Com3.  I was able to test this by trying the "Burn Bootloader" option in the tools menu of the IDE and watching the Rx/Tx LEDs blink.

I may go back in grab all the files off that old computer, and then try again, or I may start from scratch?

There seems to be a new Arduino to Android project on Hackaday again. It is using bluetooth, so I will give it a go.

I'll try to keep motivated, and put up code this time.

The Bluetooth device is called linvor 00:11:04:15:10:87, and the login is 1234.  I believe the bluetooth shield is still on 9600 baud.  I keep getting the error:

   avrdude: stk500_getsync(): not in sync: resp=0x00

with the shields on. With the shields off, it works fine. Maybe I need to turn off the bluetooth shield while I am uploading code? There is a switch, one side labeled "to board" the other labeled "to ft232". I can upload the code, with the switch on the "to ft232" side, and get no errors. I can also get serial data from the board to my phone using the serial monitor, without changing the switch. I don't receive anything from the phone though.

This is the code that is included from the Arduino to Android project


/*
 PROJECT: Andruino 0.1 Alpha
 PROGRAMMER: Hazim Bitar (techbitar at gmail dot com)
 DATE: July1, 2013
 FILE: Andruino.ino
 LICENSE: Public domain
*/

#define START_CMD_CHAR '*'
#define END_CMD_CHAR '#'
#define DIV_CMD_CHAR '|'
#define CMD_DIGITALWRITE 10
#define CMD_ANALOGWRITE 11
#define CMD_TEXT 12
#define CMD_READ_ARDUINO 13
#define MAX_COMMAND 20  // max command number code. used for error checking.
#define MIN_COMMAND 10  // minimum command number code. used for error checking. 
#define IN_STRING_LENGHT 40
#define MAX_ANALOGWRITE 255
#define PIN_HIGH 3
#define PIN_LOW 2

String inText;

void setup() {
  Serial.begin(9600);
  Serial.println("Andruino 0.1 Alpha by TechBitar (2013).");
  Serial.flush();
}

void loop()
{
  Serial.flush();
  int ard_command = 0;
  int pin_num = 0;
  int pin_value = 0;

  char get_char = ' ';  //read serial

  // wait for incoming data
  if (Serial.available() < 1) return; // if serial empty, return to loop().

  // parse incoming command start flag 
  get_char = Serial.read();
  if (get_char != START_CMD_CHAR) return; // if no command start flag, return to loop().

  // parse incoming command type
  ard_command = Serial.parseInt(); // read the command
  
  // parse incoming pin# and value  
  pin_num = Serial.parseInt(); // read the pin
  pin_value = Serial.parseInt();  // read the value

  // 1) GET TEXT COMMAND FROM ANDRUINO
  if (ard_command == CMD_TEXT){   
    inText =""; //clears variable for new input   
    while (Serial.available())  {
      char c = Serial.read();  //gets one byte from serial buffer
      delay(5);
      if (c == END_CMD_CHAR) { // if we the complete string has been read
        // add your code here
        break;
      }              
      else {
        if (c !=  DIV_CMD_CHAR) {
          inText += c; 
          delay(5);
        }
      }
    }
  }

  // 2) GET digitalWrite DATA FROM ANDRUINO
  if (ard_command == CMD_DIGITALWRITE){  
    if (pin_value == PIN_LOW) pin_value = LOW;
    else if (pin_value == PIN_HIGH) pin_value = HIGH;
    else return; // error in pin value. return. 
    set_digitalwrite( pin_num,  pin_value);  // Uncomment this function if you wish to use 
    return;  // return from start of loop()
  }

  // 3) GET analogWrite DATA FROM ANDRUINO
  if (ard_command == CMD_ANALOGWRITE) {  
    analogWrite(  pin_num, pin_value ); 
    // add your code here
    return;  // Done. return to loop();
  }

  // 4) SEND DATA TO ANDRUINO
  if (ard_command == CMD_READ_ARDUINO) { 
    // char send_to_android[] = "Place your text here." ;
    // Serial.println(send_to_android);   // Example: Sending text
    Serial.print(" Analog 0 = "); 
    Serial.println(analogRead(A0));  // Example: Read and send Analog pin value to Arduino
    return;  // Done. return to loop();
  }
}

// 2a) select the requested pin# for DigitalWrite action
void set_digitalwrite(int pin_num, int pin_value)
{
  switch (pin_num) {
  case 13:
    pinMode(13, OUTPUT);
    digitalWrite(13, pin_value);  
    // add your code here      
    break;
  case 12:
    pinMode(12, OUTPUT);
    digitalWrite(12, pin_value);   
    // add your code here       
    break;
  case 11:
    pinMode(11, OUTPUT);
    digitalWrite(11, pin_value);         
    // add your code here 
    break;
  case 10:
    pinMode(10, OUTPUT);
    digitalWrite(10, pin_value);         
    // add your code here 
    break;
  case 9:
    pinMode(9, OUTPUT);
    digitalWrite(9, pin_value);         
    // add your code here 
    break;
  case 8:
    pinMode(8, OUTPUT);
    digitalWrite(8, pin_value);         
    // add your code here 
    break;
  case 7:
    pinMode(7, OUTPUT);
    digitalWrite(7, pin_value);         
    // add your code here 
    break;
  case 6:
    pinMode(6, OUTPUT);
    digitalWrite(6, pin_value);         
    // add your code here 
    break;
  case 5:
    pinMode(5, OUTPUT);
    digitalWrite(5, pin_value); 
    // add your code here       
    break;
  case 4:
    pinMode(4, OUTPUT);
    digitalWrite(4, pin_value);         
    // add your code here 
    break;
  case 3:
    pinMode(3, OUTPUT);
    digitalWrite(3, pin_value);         
    // add your code here 
    break;
  case 2:
    pinMode(2, OUTPUT);
    digitalWrite(2, pin_value); 
    // add your code here       
    break;      
    // default: 
    // if nothing else matches, do the default
    // default is optional
  } 
}






To do more testing, I probably need to add some LED's or something.

That is all for tonight, it is getting late.




tom