diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 56d61d6..b188217 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -1,7 +1,7 @@ #!/bin/bash -protoc -I=proto --python_out meshtastic mesh.proto portnums.proto +protoc -I=proto --python_out meshtastic `ls proto/*.proto` # workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628 diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py index a574e67..6dd89d4 100644 --- a/meshtastic/__init__.py +++ b/meshtastic/__init__.py @@ -190,7 +190,9 @@ class MeshInterface: toRadio = mesh_pb2.ToRadio() # FIXME add support for non broadcast addresses - if isinstance(destinationId, int): + if destinationId is None: + raise Exception("destinationId must not be None") + elif isinstance(destinationId, int): nodeNum = destinationId elif destinationId == BROADCAST_ADDR: nodeNum = BROADCAST_NUM @@ -473,7 +475,7 @@ class MeshInterface: self._getOrCreateByNum(asDict["from"])["position"] = p # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg - if asDict["decoded"]["data"]["user"] == portnums_pb2.PortNum.NODEINFO_APP: + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.NODEINFO_APP: topic = "meshtastic.receive.user" pb = mesh_pb2.User() pb.ParseFromString(meshPacket.decoded.data.payload) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 550cfba..303238b 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -1,7 +1,7 @@ #!python3 import argparse -from . import SerialInterface, TCPInterface, BLEInterface, test +from . import SerialInterface, TCPInterface, BLEInterface, test, remote_hardware import logging import sys from pubsub import pub @@ -138,10 +138,20 @@ def onConnected(interface): interface.setOwner(args.setowner) if args.sendtext: - print(f"Sending text message {args.sendtext} to {args.dest}") - interface.sendText(args.sendtext, args.dest, + print(f"Sending text message {args.sendtext} to {args.destOrAll}") + interface.sendText(args.sendtext, args.destOrAll, wantAck=True, wantResponse=True) + if args.gpiowrb: + bitmask = 0 + bitval = 0 + for wrpair in (args.gpiowrb or []): + bitmask |= 1 << int(wrpair[0]) + bitval |= int(wrpair[1]) << int(wrpair[0]) + print(f"Writing GPIO mask 0x{bitmask:x} with value 0x{bitval:x} to {args.dest}") + rhc = remote_hardware.RemoteHardwareClient(interface) + rhc.writeGPIOs(args.dest, bitmask, bitval) + if args.set or args.setstr or args.setchan or args.seturl or args.router != None: closeNow = True @@ -260,7 +270,7 @@ def main(): "--setowner", help="Set device owner name", action="store") parser.add_argument( - "--dest", help="The destination node id for the --send commands, if not set '^all' is assumed", default="^all") + "--dest", help="The destination node id for any sent commands, if not set '^all' is assumed", default=None) parser.add_argument( "--sendtext", help="Send a text message") @@ -269,6 +279,9 @@ def main(): "--reply", help="Reply to received messages", action="store_true") + parser.add_argument( + "--gpiowrb", nargs=2, help="Set a particlar GPIO # to 1 or 0", action='append') + parser.add_argument( "--settime", help="Set the real time clock on the device", action="store_true") @@ -295,6 +308,11 @@ def main(): args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) + # Some commands require dest to be set, so we now use destOrAll for more lenient commands + args.destOrAll = args.dest + if not args.destOrAll: + args.destOrAll = "^all" + if not args.seriallog: if args.info or args.set or args.setstr or args.setchan or args.sendtext or args.router != None or args.qr: args.seriallog = "none" # assume no debug output in this case diff --git a/meshtastic/portnums_pb2.py b/meshtastic/portnums_pb2.py index 676d05a..c01bf60 100644 --- a/meshtastic/portnums_pb2.py +++ b/meshtastic/portnums_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0eportnums.proto*\x88\x01\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x0c\n\x08GPIO_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x12\n\rIP_TUNNEL_APP\x10\x80\x08\x62\x06proto3' + serialized_pb=b'\n\x0eportnums.proto*\x93\x01\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x12\n\rIP_TUNNEL_APP\x10\x80\x08\x62\x06proto3' ) _PORTNUM = _descriptor.EnumDescriptor( @@ -41,7 +41,7 @@ _PORTNUM = _descriptor.EnumDescriptor( type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='GPIO_APP', index=2, number=2, + name='REMOTE_HARDWARE_APP', index=2, number=2, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), @@ -69,14 +69,14 @@ _PORTNUM = _descriptor.EnumDescriptor( containing_type=None, serialized_options=None, serialized_start=19, - serialized_end=155, + serialized_end=166, ) _sym_db.RegisterEnumDescriptor(_PORTNUM) PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM) UNKNOWN_APP = 0 TEXT_MESSAGE_APP = 1 -GPIO_APP = 2 +REMOTE_HARDWARE_APP = 2 POSITION_APP = 3 NODEINFO_APP = 4 PRIVATE_APP = 256 diff --git a/meshtastic/remote_hardware.py b/meshtastic/remote_hardware.py new file mode 100644 index 0000000..dca05af --- /dev/null +++ b/meshtastic/remote_hardware.py @@ -0,0 +1,33 @@ + +from . import portnums_pb2, remote_hardware_pb2 + +""" +This is the client code to control/monitor simple hardware built into the +meshtastic devices. It is intended to be both a useful API/service and example +code for how you can connect to your own custom meshtastic services +""" +class RemoteHardwareClient: + + def __init__(self, iface): + """ + Constructor + + iface is the already open MeshInterface instance + """ + self.iface = iface + + + def writeGPIOs(self, nodeid, mask, vals): + """ + Write the specified vals bits to the device GPIOs. Only bits in mask that + are 1 will be changed + """ + r = remote_hardware_pb2.HardwareMessage() + r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS + r.gpio_mask = mask + r.gpio_value = vals + return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True) + + def readGPIOs(self, nodeid, mask): + """Read the specified bits from GPIO inputs on the device""" + pass \ No newline at end of file diff --git a/meshtastic/remote_hardware_pb2.py b/meshtastic/remote_hardware_pb2.py new file mode 100644 index 0000000..53f3e4c --- /dev/null +++ b/meshtastic/remote_hardware_pb2.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: remote_hardware.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='remote_hardware.proto', + package='', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x15remote_hardware.proto\"\xca\x01\n\x0fHardwareMessage\x12\"\n\x03typ\x18\x01 \x01(\x0e\x32\x15.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x62\x06proto3' +) + + + +_HARDWAREMESSAGE_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='HardwareMessage.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNSET', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='WRITE_GPIOS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='WATCH_GPIOS', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='GPIOS_CHANGED', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='READ_GPIOS', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='READ_GPIOS_REPLY', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=120, + serialized_end=228, +) +_sym_db.RegisterEnumDescriptor(_HARDWAREMESSAGE_TYPE) + + +_HARDWAREMESSAGE = _descriptor.Descriptor( + name='HardwareMessage', + full_name='HardwareMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='typ', full_name='HardwareMessage.typ', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gpio_mask', full_name='HardwareMessage.gpio_mask', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gpio_value', full_name='HardwareMessage.gpio_value', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _HARDWAREMESSAGE_TYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=26, + serialized_end=228, +) + +_HARDWAREMESSAGE.fields_by_name['typ'].enum_type = _HARDWAREMESSAGE_TYPE +_HARDWAREMESSAGE_TYPE.containing_type = _HARDWAREMESSAGE +DESCRIPTOR.message_types_by_name['HardwareMessage'] = _HARDWAREMESSAGE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), { + 'DESCRIPTOR' : _HARDWAREMESSAGE, + '__module__' : 'remote_hardware_pb2' + # @@protoc_insertion_point(class_scope:HardwareMessage) + }) +_sym_db.RegisterMessage(HardwareMessage) + + +# @@protoc_insertion_point(module_scope) diff --git a/proto b/proto index 7652d96..f38b8e3 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 7652d96bc7d4d3f027f29a9b3e55cbd58778a51d +Subproject commit f38b8e3040528aaf1f1b4b9024ff8df2e14ba961