AccelerationConsortium / ac-training-lab

Codebase for controlling and managing the Acceleration Consortium (AC) Training Lab.
https://ac-training-lab.readthedocs.io/
MIT License
8 stars 2 forks source link

RS232 control of digital scale #20

Open sgbaird opened 2 months ago

sgbaird commented 2 months ago

Using https://www.waveshare.com/wiki/Pico-2CH-RS232 and US Solid 3kg scale

EDIT: one Pico-2CH-RS232 is available (in 3D printer room). Both the scale and the adapter that came with the RS232 module are male, so I also ordered F-F adapters. The same applies for the A&D scale.

I had trouble finding the instructions/manual for the US Solid scale online. Here is a scan of a paper copy:

uss-dbs-series-operation-manual.pdf

The directions are somewhat sparse, perhaps because continuous output is essentially the only option (compared with A&D which has a lot more functionality exposed).

Related microcourse module: https://ac-microcourses.readthedocs.io/en/latest/courses/robotics/3.2-serial-communication.html

sgbaird commented 2 months ago

Received the female female adapters for the DB9 RS232 plugs. This is because the waveshare module and the digital scale both have male adapters.

All the hardware should be available here.

There's also a precision screwdriver set on the desk that can be used.

EDIT: Also adding the 5V charger and usb cable to the mix.

evelynnesher commented 2 months ago


Trying to connect scale to raspberry pi + base but cannot get it to connect.

Sorry for the above notifications, I was just playing with the settings.

sgbaird commented 2 months ago

Hi @evelynnesher, here is the documentation for the PICO-2CH-RS232 module. Connecting the scale directly to the Pico W won't work. You also won't need to user the Maker Pi Pico base. Just the Pico W and the RS232 module (I think they're already plugged in to each other).

The resources from the first post should also help:

Using waveshare.com/wiki/Pico-2CH-RS232 and US Solid 3kg scale

EDIT: one Pico-2CH-RS232 is available (in 3D printer room). Both the scale and the adapter that came with the RS232 module are male, so I also ordered F-F adapters. The same applies for the A&D scale.

I had trouble finding the instructions/manual for the US Solid scale online. Here is a scan of a paper copy:

uss-dbs-series-operation-manual.pdf

The directions are somewhat sparse, perhaps because continuous output is essentially the only option (compared with A&D which has a lot more functionality exposed).

Related microcourse module: ac-microcourses.readthedocs.io/en/latest/courses/robotics/3.2-serial-communication.html

You will need to check the manual for the scale to see what UART command is required (it differs from the example shown).

evelynnesher commented 2 months ago

@sgbaird I think I don't have a Industrial RS232 to 3.3V TTL Converter.

sgbaird commented 2 months ago

@sgbaird I think I don't have a Industrial RS232 to 3.3V TTL Converter.

Correct, I'm mostly referring to the code snippet. You have this one https://www.waveshare.com/pico-2ch-rs232.htm (an alternative to the one you linked).

evelynnesher commented 2 months ago

setup2 wire setup Did I set this up correctly? Still having connection issues

sgbaird commented 2 months ago

I think that's correct. Could you describe the connection issue in more detail?

evelynnesher commented 2 months ago

When all the parts connect, it is not reading any data. image Specifically, I make sure to check the scale is connected but uart.any() is always empty. I also make sure to follow the instructions on page 7 of the scale operations manual, data output settings.

sgbaird commented 2 months ago

Can you share the full code snippet that you're running? Use three backticks to apply code formatting

evelynnesher commented 2 months ago

The following code from module 3.2 of the microcourse:


import machine
import utime

# Initialize UART
uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5))

# Send the "Q" command and read response
uart.write(b"Q\r\n")
utime.sleep(0.1)
if uart.any():
    print(uart.read().decode("utf-8"))

# Continuous reading loop
while True:
    if uart.any():
        print(uart.read().decode("utf-8"))
    utime.sleep(1)
sgbaird commented 2 months ago

You can remove the "Q" command. That doesn't apply to the US solid scale. It may not affect anything, but will make debugging easier.

Assuming you've set the scale up in continuous output mode per the instruction manual, it may be worth using the uartassist software that was mentioned and connect directly to your computer, however you'll need a different type of cable. I think I have the required connectors in the office. I'll set those aside for you to pick up (likely tomorrow).

sgbaird commented 2 months ago

Actually, it looks like you have it connected into channel 0, however you're specifying the pins for channel 1. Check the pin legend on the Pico 2ch rs232 wiki.

The code from that draft was generated by ChatGPT (meant to be adapted), and I haven't tested the scale or the rs232 module yet, so it's not a drop-in replacement.

evelynnesher commented 2 months ago

@sgbaird I am trying to correctly set up the scale to send continuous output. I follow the continuous output instructions (pasted below/ found on page 7 of the instructions manual), once I get to the last step and I click TARE, it sends the display to C06-0. I provided the reference for C06 displays.

image

image

I had ChatGPT write Thonny code that checks if the scale is connected. I believe my issue either lies in the continuous output or the physical setup.


import utime

# Initialize UART1 on pins GP4 (TX) and GP5 (RX)
uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5))

def check_scale_connection():
    print("Checking if the scale is connected...")

    # Try reading data from the UART
    if uart.any():  # If there's data available on the UART
        response = uart.read()  # Read the data
        if response:
            try:
                # Try to decode the response as UTF-8
                decoded_data = response.decode("utf-8").strip()
                print("Scale connected. Data received:", decoded_data)
            except UnicodeDecodeError:
                # If the data cannot be decoded, show it in raw form
                print("Scale connected. Non-text data received:", response)
        else:
            print("Scale connected, but no data received.")
    else:
        print("No data received. The scale may not be connected or may not be sending data.")

# Run the check in a loop to continuously check if the scale is connected
while True:
    check_scale_connection()
    utime.sleep(2)  # Check every 2 seconds
sgbaird commented 2 months ago

Thanks for the info! I'm not sure what the issue is. Happy to take a look when you're nearby. Otherwise, you could take a video of the full process and I can look over it.

evelynnesher commented 2 months ago

https://github.com/user-attachments/assets/60bff6e9-e8eb-4627-bbc4-e17e1bfe4ec2

I added a video of me following the manual instructions. As for the physical connection, it is the same as the picture I added yesterday except it is in channel 1 instead of 0 to correspond with the code. I have two adaptations of the F-F adapter. When I take the screws out it falls apart/ doesn’t stay inside the port in the scale so I just play around with it until it stays.

IMG_2139

https://github.com/user-attachments/assets/ab9fdf10-f28d-4be1-8f42-a9c7c035b08f

sgbaird commented 1 month ago

It's different from what the instructions say, but maybe worth trying a long press of the tare at the end, or ignoring the last step of pressing tare. Directly connecting to a computer with a DB9 to USB-A cable (in one of the boxes in the room with the 3D printer), and using the uartassist software will likely help with the troubleshooting. We also have a second type of scale that probably has better documentation.

sgbaird commented 1 month ago

Aside: Some additional Pico 2-channel RS232 modules came in

PXL_20241001_154949742.jpg

evelynnesher commented 1 month ago

I’ve gone through every combination of buttons, so the long TARE at the end isn’t the solution. I’ll stop by around 3 tomorrow to pick up the new materials. Will you be available then? If so, we can take a look together; if not, I’ll keep trying to sort it out on my own.

sgbaird commented 1 month ago

Here's the cable that you should be able to use to connect directly to your computer for troubleshooting. It's in the room with a 3D printer

PXL_20241001_201422446.jpg

There is a second scale that may be worth troubleshooting with. This one is more sensitive and needs to stay in the office. Instructions are also different, but I think there are links on the micro course website. This is one that we know can do serial communication.

PXL_20241001_204033333.jpg

evelynnesher commented 1 month ago

Thank you! I’d like to start with the first one you provided. If that doesn’t work, I’ll come back and try the second scale.

evelynnesher commented 1 month ago

image UartAssist Software worked. Worked with Channel COM9 with the baud rate, parity, data bits, stop bits, and flow control set to the values specified by the scale manual.

sgbaird commented 1 month ago

Amazing! Narrows things down for sure.

sgbaird commented 1 month ago

And these were the correct instructions?

https://github.com/AccelerationConsortium/ac-training-lab/issues/20#issuecomment-2375510617

evelynnesher commented 1 month ago

These were the instructions available for using the software image To use the UartAssist Software, you configure the serial options (top left) then click open and it gives you continuous input.

evelynnesher commented 1 month ago

I'm trying everything to debug the issue. Could the problem be related to the fact that the scale's output is 110V/220V while the Pico operates at 3.3V~5V? Could this voltage difference be causing the communication issue? Scale technical parameters: image Pico-2CH-RS232 Specifications: image

evelynnesher commented 1 month ago

As part of my debugging efforts, I wrote some code to check the connection with the scale. The uart.any() method checks for available data in the UART and returns the number of bytes present in the receive buffer. However, it hasn’t provided any clarity, as it consistently returns False/zero. This means that there is no data available (I’m not sure if you already know this, but I wanted to clarify since you mentioned you're not familiar with the RS232 module). Any suggestions/next steps? image


import machine
import utime

# Initialize UART
#uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5))
uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5), bits=8, parity=None, stop=1)

#pins 4 and 5, 8 and 9 work
# Function to check if the scale is connected
def check_scale_connection():

    utime.sleep(0.1)  

    if uart.any():  # Check if there's any response
        response = uart.read()  # Read the response
        if response:
            print("Scale connected. Response:", response.decode("utf-8").strip())
        else:
            print("Scale is not responding.")
    else:
        print("No response from the scale. Check connections.")

# Check if the scale is connected
check_scale_connection()
sgbaird commented 1 month ago

Since we know it's not the scale, maybe reach out to the company for support. There's a "submit ticket" button at the bottom of https://www.waveshare.com/wiki/Pico-2CH-RS232.

sgbaird commented 1 month ago

Various resources:

Demo code from the "Download Example" section from the wiki, using https://files.waveshare.com/upload/5/5a/Pico_2CH_RS232_Code.7z, which can be downloaded manually and viewed (Windows seemed to do fine opening it up even in the archived state, not sure if that's because I did something special a while back to get it to work with 7z, or if it's a default for Windows now). The following Python example is given:

from machine import UART, Pin
import time

uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))

uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))

txData = b'RS232 receive test...\r\n'
uart0.write(txData)
time.sleep(0.1)
while True:
        #rxData = bytes()
    while uart0.any() > 0:    #Channel 0 is spontaneous and self-collecting
        rxData0 = uart0.read()
        uart0.write("{}".format(rxData0.decode('utf-8')))
        print(rxData0)
        if(uart0.any()==0):
            uart0.write("\r\n")
    while uart1.any() > 0:   #Channel 1 is spontaneous and self-collecting
        rxData1 = uart1.read()
        uart1.write("{}".format(rxData1.decode('utf-8')))
        print(rxData1)
        if(uart1.any()==0):
            uart1.write("\r\n")

Some other code examples:

EDIT: Verified that if I connect TX and RX together, the code works. For example, TX0 <--> RX1 and TX1 <--> RX2 with the code example above.

In terms of the US Solid scale, it may be that the black wire is actually TX, not GND... (i.e., the three dupont cables are flipped). I'm able to get some reading by switching.

EDIT: the female-female DB9 adapter needed to have screws removed to be able to attach to the scale. It would be better to get some connectors that don't have the threaded nuts on each side.

sgbaird commented 1 month ago

I think the issue may actually be the use of the female-female adapter, in that it flips the connections from what is expected.. this was my bad. I'm planning to get something like the following so we can have more control:

Futheda 5PCS D-SUB DB9 RS232 Interface Breakout Board Connector 9-pin 2-Row Female RS-232 Serial Port Solderless Terminal Block Adapter with Positioning Nuts https://a.co/d/6LEEta6

Likewise, the existing connector could be modified, but maybe better to just wait and go with the one above.

evelynnesher commented 1 month ago

Submitted the following ticket to Pico-2CH-RS232 Wiki page on October 11, 2024.

Hi, I hope you're doing well.

I'm working on connecting a Pico-2CH-RS232 module to a U.S. Solid Digital Scale. I'm following the RS232 module instructions for continuous output as outlined in the scale's manual, aiming to have the Pico collect and manage the data. The digital scale is connected to Channel 1 on the Pico Base using a Dupont 3-pin to DB-9 wire cable. We've confirmed that the scale works by testing the continuous output with UartAssist software.

However, the main issue we're facing is that, when using MicroPython on Thonny, we cannot read any connection from the scale. After following the continuous output instructions from the scale manual, nothing happens. We have also tried the basic example demo code from the "Download Example" section from the wiki, using https://files.waveshare.com/upload/5/5a/Pico_2CH_RS232_Code.7z

Here’s the code I’m currently using, which continuously prints "No response from the scale. Check connections":

import machine
import utime

# Initialize UART
# uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5))
uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5), bits=8, parity=None, stop=1)

# Function to check if the scale is connected
def check_scale_connection():
while True:
if uart.any(): # Check if there's any response
response = uart.read() # Read the response
if response:
print("Scale connected. Response:", response.decode("utf-8").strip())
else:
print("Scale is not responding.")
else:
print("No response from the scale. Check connections.")

# Check if the scale is connected
check_scale_connection()

Here’s the GitHub thread where we've outlined all the troubleshooting steps we've taken so far: https://github.com/AccelerationConsortium/ac-training-lab/issues/20

Please let me know if you need further information or have any suggestions on how I can proceed.

Best regards, Evelyn

sgbaird commented 1 month ago

The new part to help with troubleshooting should be here on Monday.

evelynnesher commented 1 month ago

https://github.com/user-attachments/assets/9da69e02-e084-4394-aefa-b2872de8c110

IMG_2538 IMG_2537

I got it working! I used the Female DB9 breakout board adapter with three male-to-male jumper wires. I connected GND to GND, TX on the breakout board to RX on the Pico, and RX on the breakout board to TX on the Pico. I didn’t need to use the DB9 to multicore cable. I attached a video demonstrating the continuous output and included the code I used.


import machine
import utime

# Initialize UART
uart = machine.UART(1, baudrate=9600, tx=machine.Pin(4), rx=machine.Pin(5))

# Send the "Q" command and read response
uart.write(b"Q\r\n")
utime.sleep(0.1)
if uart.any():
    print(uart.read().decode("utf-8"))

# Continuous reading loop
while True:
    if uart.any():
        print(uart.read().decode("utf-8"))
    utime.sleep(1)
evelynnesher commented 1 month ago

IMG_2546

By using the new adapter, I realized that the connection issue wasn’t due to the Female-Female adapter, but rather with the TX-RX connection. Instead of directly plugging the DB9 into TX1, RX1, and GND on the microcontroller (as shown in the earlier pictures), I used male-male jumper cables with our original setup. I connected the jumper wires as shown in the picture: black to black and red to yellow.

Review of differences to help decide which setup is better suited for our needs: Assembly: First Setup: A screwdriver was needed for assembly, adding a minor step but ensuring tighter, more secure connections. Second Setup: No screwdriver was required Both were very easy to setup.

Adapter Durability: In the second setup, the female-female adapter falls apart if not held together. This happens because the nuts on the adapter had to be removed to fit. While this hasn't caused disconnection issues yet, it may pose a risk during long-term use.

Wire Bending: First Setup: Both sides of the wires bend slightly when secured in the terminal blocks, but it doesn't affect functionality. Second Setup: Only one side of the wires bends, making it slightly cleaner, but again, it doesn't impact functionality.

Data Relay Speed: The data speed appears to be the same in both setups, but I will be further testing to confirm.

sgbaird commented 1 month ago

Way to go! That's great you figured it out on both setups. Great progress.

sgbaird commented 1 month ago

Let's ditch the F-F adapter setup. I think sticking with the terminal breakout is fine.

I wouldn't worry much about the data transfer speed. Instead, the next priority will be setting up MQTT and a hugging face spaces web app for it.

This will be very similar to the fan control demo, except without the "send command" aspect. Will share some links.

sgbaird commented 3 weeks ago

Now that you have the scale working, can you start working on an MQTT + gradio web app implementation for one way streaming of the scale data? (See https://ac-training-lab.readthedocs.io/en/latest/devices/setup_iolt_devices.html)

evelynnesher commented 3 weeks ago

Definitely, I’ve started already. You want me to use Gradio instead of Streamlit?

sgbaird commented 3 weeks ago

Great, and yes - it would be best to switch over (see https://github.com/AccelerationConsortium/ac-training-lab/issues/77#issuecomment-2429034334).

evelynnesher commented 3 weeks ago

I successfully established the MQTT connection and will now start implementing Gradio. It's currently set up to send the following data:

{
    "weight": "15.5g",
    "time": "2024-10-30 12:34:56",
    "device_id": "ambiguous-goose"
}

On HiveMQ, I can see the following information organized under the columns Message, Topic, and QoS: image

For Hugging Face Spaces, would you like a button to turn continuous data on and off, and should the scale send just the weight or include the timestamp and other information? Should the output appear below the button, or would you prefer it to be displayed elsewhere?

sgbaird commented 3 weeks ago

Nice! That's great. I think weight and timestamp works, and you can use a unique ID from the Pico W in the topic structure and leave off the device ID. Something like <US Solid product ID>/<Pico ID>.

For id, see https://github.com/sparks-baird/self-driving-lab-demo/blob/3d45417f1832679f84044f52e3c64daeb5437ca3/src/public_mqtt_sdl_demo/main.py#L63. For UTC timestamp, see (https://github.com/sparks-baird/self-driving-lab-demo/blob/3d45417f1832679f84044f52e3c64daeb5437ca3/src/public_mqtt_sdl_demo/lib/sdl_demo_utils.py#L188 and https://github.com/sparks-baird/self-driving-lab-demo/blob/3d45417f1832679f84044f52e3c64daeb5437ca3/src/public_mqtt_sdl_demo/main.py#L106) (i.e., you should synchronize the time via ntptime before calling).

For Hugging Face, I think as soon as it starts up, then start streaming the weigh data. You will want to try to avoid re-instantiating the MQTT client within a session.

Also, I recommend using some variation of the plotting code from https://huggingface.co/spaces/AccelerationConsortium/fan-control

evelynnesher commented 3 weeks ago

Needs Refresh: imageimage Continuous: image image

I’ve completed the Gradio app in a personal space on my account. I made two versions: one displays data when you click the refresh button, and the other updates continuously. The continuous one currently updates slowly, but I'm working on sending real-time updates.

Let me know what changes to make. I’ll also need access to the Acceleration Consortium space so I can implement it there.

Currently, I run main.py on Thonny to collect the data and send it to HiveMQ, while I run app.py on Hugging Face Spaces to create the graph that retrieves data from HiveMQ.

sgbaird commented 3 weeks ago

Nice! Can you provide links to these two spaces?

evelynnesher commented 2 weeks ago

Continuously updating graph : https://huggingface.co/spaces/evelyn-nesher19/scale-test-continous This graph updates smoothly in real-time and includes a refresh button for manual updates.

Manual Refresh Needed: https://huggingface.co/spaces/evelyn-nesher19/scale-test-refresh This version has a "connection status" button and requires manual refreshing to display the latest data.

sgbaird commented 2 weeks ago

Maybe use a sliding window for the data, with a history of ~30 seconds? I don't think a manual refresh is needed in this case. Maybe have a look at the main.py for the fan control demo in the AC training lab repo (nested under src). You can reduce the time sampling so that data points get sent every 2-3 seconds for example.

Overall looks clean and good!

evelynnesher commented 2 weeks ago

image I've embedded the space into Gather Town!

sgbaird commented 2 weeks ago

Nice! Looks great 😊

I noticed something in the logs when trying to click connect:

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/gradio/queueing.py", line 624, in process_events
    response = await route_utils.call_process_api(
  File "/usr/local/lib/python3.10/site-packages/gradio/route_utils.py", line 323, in call_process_api
    output = await app.get_blocks().process_api(
  File "/usr/local/lib/python3.10/site-packages/gradio/blocks.py", line 2015, in process_api
    result = await self.call_function(
  File "/usr/local/lib/python3.10/site-packages/gradio/blocks.py", line 1562, in call_function
    prediction = await anyio.to_thread.run_sync(  # type: ignore
  File "/usr/local/lib/python3.10/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
  File "/usr/local/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 2441, in run_sync_in_worker_thread
    return await future
  File "/usr/local/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 943, in run
    result = context.run(func, *args)
  File "/usr/local/lib/python3.10/site-packages/gradio/utils.py", line 865, in wrapper
    response = f(*args, **kwargs)
  File "/home/user/app/app.py", line 46, in connect_mqtt
    client.tls_set()
  File "/usr/local/lib/python3.10/site-packages/paho/mqtt/client.py", line 1302, in tls_set
    self.tls_set_context(context)
  File "/usr/local/lib/python3.10/site-packages/paho/mqtt/client.py", line 1193, in tls_set_context
    raise ValueError('SSL/TLS has already been configured.')
ValueError: SSL/TLS has already been configured.
evelynnesher commented 2 weeks ago

Fixed! I don't have access to edit AC Hugging Face Spaces Collections, so I can't place it under the "Hardware Control" Collection. Ready to set it up at the office with your HiveMQ credentials and start my next task.

sgbaird commented 2 weeks ago

Nice! If there are any updates to main.py on the microcontroller, do you mind making a PR? If not, you can ignore. The Pico W will need the Nokia credentials, and you can use the 248 broker and username/password (the public test credentials) for now. We may switch to a different one a bit later.

Also, I added the HF space to the collection.

sgbaird commented 2 weeks ago

Perhaps the next task will be the autotrickler, since that carries over the RS232 experience and is a natural extension.

evelynnesher commented 4 days ago

I have added a connection status bar. It changes in real time, and is currently live.

Here are three possible connection states: image image image