jsonpython-3.xbluetoothpybluez

JSONDecodeError Expecting value: line 1 column 1 (char 0) when receiving via Bluetooth


I am transmitting a JSON payload from an Arduino microcontroller and attempting to receive it using a Python script:

import bluetooth  #pybluez 
import json

sensor_address = "18:D9:31:YY:C7:4A"
socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
socket.connect((sensor_address, 1))
buffer = ""

print("Listening...")
while True: 
    data = socket.recv(1024)
    buffer += str(data, encoding='ascii')
    print(buffer) # used to check json payload
    try: 
        data = json.loads(buffer)
        print("Received:", data)
        buffer = ""
    except json.JSONDecodeError as e:
        print(e)
        continue

Examining the value stored in buffer before entering the try statement, I see what appears to be perfectly valid JSON:

{"a_x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-2.816794,"g_y":2.572519}{"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g_x":-1,"g_y":0.374046}

However the result of my script is only Expecting value: line 1 column 1 (char 0) repeatedly.

Why doesn't the code inside the try statement execute once a complete payload has been received?

My hunch is that at no time does a valid JSON payload appear in buffer, but instead valid payloads appear along with incomplete payloads.

Is it possible to use a regular expression to extract a valid payload from a string?


Solution

  • The data in buffer is not valid JSON so that is why you are seeing the error.

    The buffer seems to have the information in the format of a Python dictionary so you could use Python re module to extract the dictionary and then use ast.literal_eval to turn the string in to a Python dictionary.

    In the example below I've mocked the reading of the socket as I don't have your device.

    import re
    
    pattern = re.compile(r'{.*?}')
    
    
    class socket:
        """Mock reading data from socket"""
        pointer = 3
        chunk_size = 8
        feed = (b'{"a_x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}'
                b'{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-2.816794,"g_y":2.572519}'
                b'{"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g_x":-1,"g_y":0.374046}')
    
        @classmethod
        def recv(cls):
            data = cls.feed[cls.pointer:cls.pointer + cls.chunk_size]
            cls.pointer += cls.chunk_size
            return data
    
    
    def process_reading(buffer):
        match = re.search(pattern, buffer)
        start_idx, end_idx = match.span()
        reading = literal_eval(buffer[start_idx:end_idx])
        buffer = buffer[end_idx:]
        return buffer, reading
    
    
    def main():
        buffer = ''
        data = True
        while data:
            data = socket.recv()
            # print("Data:", data)
            buffer += str(data, encoding='ascii')
            print("Buffer contents", buffer)
            if re.search(pattern, buffer):
                buffer, measurement = process_reading(buffer)
                print("\tMeasurement:", measurement.get('g_x'))
    
    
    if __name__ == '__main__':
        main()
    

    This gave the following output:

    Buffer contents _x":957.
    Buffer contents _x":957.5195,"a_
    Buffer contents _x":957.5195,"a_y":-0.48
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.9
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x"
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.6259
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y"
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.3053
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.91
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.95312
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-2.816794
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-2.816794,"g_y":2
    Buffer contents _x":957.5195,"a_y":-0.488281,"a_z":315.918,"g_x":-0.625954,"g_y":-1.305344}{"a_x":961.914,"a_y":-1.953125,"a_z":297.8516,"g_x":-2.816794,"g_y":2.572519}
        Measurement: -2.816794
    Buffer contents {"a_x":9
    Buffer contents {"a_x":964.8437,
    Buffer contents {"a_x":964.8437,"a_y":3.
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"a_z":303
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g_x":-1,"
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g_x":-1,"g_y":0.3
    Buffer contents {"a_x":964.8437,"a_y":3.417969,"a_z":303.2227,"g_x":-1,"g_y":0.374046}
        Measurement: -1
    Buffer contents