diff --git a/docs/en/module/grbl.rst b/docs/en/module/grbl.rst new file mode 100644 index 00000000..8e41e4c6 --- /dev/null +++ b/docs/en/module/grbl.rst @@ -0,0 +1,202 @@ + +GRBLModule +========== + +.. include:: ../refs/module.grblmodule.ref + +GRBL 13.2 is a three-axis stepper motor driver module in the M5Stack stacking module series. It uses an ATmega328P-AU controller with three sets of DRV8825PWPR stepper motor driver chip control ways, which can drive three bipolar steppers at the same time. + +Support the following products: + +|GRBLModule| + +Micropython Example: + + .. literalinclude:: ../../../examples/module/grbl_example.py + :language: python + :linenos: + + +UIFLOW2 Example: + + |example.png| + +.. only:: builder_html + + |grbl_example.m5f2| + +class GRBLModule +---------------- + +Constructors +------------ + +.. class:: GRBLModule(address) + + Initialize the GRBLModule. + + :param hex address: The I2C address of the device. + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: GRBLModule.g_code(command) + + Send the G-code command. + + :param command: The G-code command. + + UIFLOW2: + + |g_code.png| + +.. method:: GRBLModule.get_code_time(code) + + Get the time of the code. + + :return (int): The estimated time of the command. + :param code: The G-code command + + UIFLOW2: + + |get_code_time.png| + +.. method:: GRBLModule.turn(x, y, z, speed) + + Turn the motor to a specific position. + + :param x: The position of the X motor, 1.6=360°. + :param y: The position of the Y motor, 1.6=360°. + :param z: The position of the Z motor, 1.6=360°. + :param speed: The speed of the motor. + + UIFLOW2: + + |turn.png| + +.. method:: GRBLModule.set_mode(mode) + + Set the mode of the motor. + + :param mode: The mode of the motor. + Options: + - ``Absolute``: GRBLModule.MODE_ABSOLUTE + - ``Relative``: GRBLModule.MODE_RELATIVE + + UIFLOW2: + + |set_mode.png| + +.. method:: GRBLModule.init(x_step, y_step, z_step, acc) + + Initialize the motor. + + :param x_step: The step of the X motor. + :param y_step: The step of the Y motor. + :param z_step: The step of the Z motor. + :param acc: The acceleration of the motor. + + UIFLOW2: + + |init.png| + +.. method:: GRBLModule.flush() + + Flush the buffer. + + + UIFLOW2: + + |flush.png| + +.. method:: GRBLModule.get_message() + + Get the message. + + :return (str): The message string. + + UIFLOW2: + + |get_message.png| + +.. method:: GRBLModule.get_status() + + Get the status. + + :return (str): The status string. + + UIFLOW2: + + |get_status.png| + +.. method:: GRBLModule.get_idle_state() + + Get the idle state. + + :return (bool): The idle state. + + UIFLOW2: + + |get_idle_state.png| + +.. method:: GRBLModule.get_lock_state() + + Get the lock state. + + :return (bool): The lock state. + + UIFLOW2: + + |get_lock_state.png| + +.. method:: GRBLModule.wait_idle() + + Wait until the motor is idle. + + + UIFLOW2: + + |wait_idle.png| + +.. method:: GRBLModule.unlock_alarm_state() + + Unlock the alarm state. + + + UIFLOW2: + + |unlock_alarm_state.png| + +.. method:: GRBLModule.lock() + + Lock the motor. + + + UIFLOW2: + + |lock.png| + +.. method:: GRBLModule.unlock() + + Unlock the motor. + + + UIFLOW2: + + |unlock.png| + + + +Constants +--------- + + .. data:: GRBLModule.MODE_ABSOLUTE + .. data:: GRBLModule.MODE_RELATIVE + + Motor mode + diff --git a/docs/en/module/index.rst b/docs/en/module/index.rst index 3a9d9dc0..02569e17 100644 --- a/docs/en/module/index.rst +++ b/docs/en/module/index.rst @@ -8,10 +8,14 @@ Module ain4.rst display.rst dualkmeter.rst + grbl.rst hmi.rst lora.rst + lorawan868.rst + nbiot.rst plus.rst pps.rst rca.rst relay_2.rst rs232.rst + step_motor_driver.rst diff --git a/docs/en/module/lorawan868.rst b/docs/en/module/lorawan868.rst new file mode 100644 index 00000000..d376c71f --- /dev/null +++ b/docs/en/module/lorawan868.rst @@ -0,0 +1,416 @@ + +LoRaWAN868Module +================ + +.. include:: ../refs/module.lorawan868module.ref + +COM.LoRaWAN is a LoRaWAN communication module in the M5Stack stackable module series, supporting node-to-node or LoRaWAN communication. + +Support the following products: + +|LoRaWAN868Module| + +Micropython Example: + + .. literalinclude:: ../../../examples/module/lorawan868_example_tx.py + :language: python + :linenos: + .. literalinclude:: ../../../examples/module/lorawan868_example_rx.py + :language: python + :linenos: + + +UIFLOW2 Example: + + |example.png| + +.. only:: builder_html + + |lorawan868_example_tx.m5f2| + |lorawan868_example_rx.m5f2| + +class LoRaWAN868Module +---------------------- + +Constructors +------------ + +.. class:: LoRaWAN868Module(id, port, band) + + Initialize the LoRaWANModule. + + :param int id: The UART ID to use for communication. + :param port: The UART port to use for communication, specified as a tuple of (rx, tx) pins. + :param band: The frequency to use for LoRa communication + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: LoRaWAN868Module.set_mode(mode) + + Set the mode of the LoRaWAN module. + + :param mode: The mode to set. + + UIFLOW2: + + |set_mode.png| + +.. method:: LoRaWAN868Module.set_parameters(freq, power, sf, bw, cr, preamble, crc, iq_inv, save) + + Set the parameters of the LoRaWAN module. + + :param freq: Set LoRa listening/sending frequency in Hz. + :param power: LoRa signal output power in dBm; + :param sf: Spreading factor, from 5~12 + :param bw: Bandwidth 0 – 125K, 1 – 250K, 2 – 500K; + :param cr: 1 – 4/5, 2 – 4/6, 3 – 4/7, 4 – 4/8; + :param preamble: Preamble Length from 8~65535 bit; + :param crc: 0 – disable CRC check, 1 – enable CRC check; + :param iq_inv: 0 -- not inverted, 1 – inverted; + :param save: Save parameters to FLASH, 0 – not save, 1 – save. + + UIFLOW2: + + |set_parameters.png| + +.. method:: LoRaWAN868Module.wake_up() + + Wake up the device through a serial port interrupt. After resetting, the device is in sleep state. In theory, sending any data through the serial port can trigger the interrupt and wake up the device. + + + UIFLOW2: + + |wake_up.png| + +.. method:: LoRaWAN868Module.sleep() + + Put the device into low-power mode. + + + UIFLOW2: + + |sleep.png| + +.. method:: LoRaWAN868Module.reset() + + Reset the device. + + + UIFLOW2: + + |reset.png| + +.. method:: LoRaWAN868Module.restore_factory_settings() + + Restore the device to factory settings. The parameters will reset and the device will enter sleep mode after response ends. + + + UIFLOW2: + + |restore_factory_settings.png| + +.. method:: LoRaWAN868Module.set_copyright(enable) + + Enable or disable copyright information print when boot loader mode begins. Default is enable. + + :param bool enable: Set True to enable, False to disable. + + UIFLOW2: + + |set_copyright.png| + +.. method:: LoRaWAN868Module.set_auto_low_power(enable) + + Enable or disable automatic low-power mode. Default is enable. + + :param bool enable: Set True to enable, False to disable. + + UIFLOW2: + + |set_auto_low_power.png| + +.. method:: LoRaWAN868Module.query_chip_id() + + Query the unique ID of the chip, which can be used to query the corresponding serial number. + + + UIFLOW2: + + |query_chip_id.png| + +.. method:: LoRaWAN868Module.enable_rx(timeout) + + Enable the LoRaWAN module to receive data. + + :param int timeout: The timeout for the receive operation. + + UIFLOW2: + + |enable_rx.png| + +.. method:: LoRaWAN868Module.set_deveui(deveui) + + Set or query the DevEui. DevEui must be 16 hex characters (0-9, A-F). + + :param deveui: The DevEui to set. If None, query the current DevEui. + + UIFLOW2: + + |set_deveui.png| + +.. method:: LoRaWAN868Module.set_appeui(appeui) + + Set or query the AppEui. AppEui must be 16 hex characters (0-9, A-F). + + :param appeui: The AppEui to set. If None, query the current AppEui. + + UIFLOW2: + + |set_appeui.png| + +.. method:: LoRaWAN868Module.set_appkey(appkey) + + Set or query the AppKey. AppKey must be 32 hex characters (0-9, A-F). + + :param appkey: The AppKey to set. If None, query the current AppKey. + + UIFLOW2: + + |set_appkey.png| + +.. method:: LoRaWAN868Module.set_nwkskey(nwkskey) + + Set or query the NwkSKey. NwkSKey must be 32 hex characters (0-9, A-F). + + :param nwkskey: The NwkSKey to set. If None, query the current NwkSKey. + + UIFLOW2: + + |set_nwkskey.png| + +.. method:: LoRaWAN868Module.set_appskey(appskey) + + Set or query the AppSKey. AppSKey must be 32 hex characters (0-9, A-F). + + :param appskey: The AppSKey to set. If None, query the current AppSKey. + + UIFLOW2: + + |set_appskey.png| + +.. method:: LoRaWAN868Module.set_devaddr(devaddr) + + Set or query the DevAddr. DevAddr must be 8 hex characters (0-9, A-F). + + :param devaddr: The DevAddr to set. If None, query the current DevAddr. + + UIFLOW2: + + |set_devaddr.png| + +.. method:: LoRaWAN868Module.set_otaa_mode(enable) + + Set or query the OTAA mode. 1 for OTAA mode, 0 for ABP mode. + + :param bool enable: Set True for OTAA mode, False for ABP mode. + + UIFLOW2: + + |set_otaa_mode.png| + +.. method:: LoRaWAN868Module.set_adr(enable) + + Enable or disable the ADR (Adaptive Data Rate) function. Default is enabled. + + :param bool enable: Set True to enable ADR, False to disable. + + UIFLOW2: + + |set_adr.png| + +.. method:: LoRaWAN868Module.set_channel_mask(mask) + + Set or query the LoRaWAN working channel mask. + + :param mask: The channel mask in hexadecimal format, e.g., 0000000000000000000000FF for channels 0~7. + + UIFLOW2: + + |set_channel_mask.png| + +.. method:: LoRaWAN868Module.join_network() + + Join the network using OTAA (Over-The-Air Activation). This command triggers the join process. + + + UIFLOW2: + + |join_network.png| + +.. method:: LoRaWAN868Module.set_duty_cycle(cycle) + + Set or query the communication cycle in milliseconds. For example, 60000 means communication every 60 seconds. + + :param cycle: The communication cycle in milliseconds. + + UIFLOW2: + + |set_duty_cycle.png| + +.. method:: LoRaWAN868Module.set_class_mode(mode) + + Set or query the device's communication mode. Only Class A or Class C are valid. + + :param mode: Set "A" for Class A or "C" for Class C. + + UIFLOW2: + + |set_class_mode.png| + +.. method:: LoRaWAN868Module.set_ack(enable) + + Enable or disable the ACK receipt function. If enabled, the device waits for acknowledgment from the gateway. + + :param bool enable: Set True to enable ACK, False to disable. + + UIFLOW2: + + |set_ack.png| + +.. method:: LoRaWAN868Module.set_app_port(port) + + Set or query the application port (fport) for upstream data. Valid range is 0~255. + + :param port: The application port to set. + + UIFLOW2: + + |set_app_port.png| + +.. method:: LoRaWAN868Module.set_retransmission_count(count) + + Set or query the number of retransmissions if communication fails. The valid range is 3~8. + + :param count: The number of retransmissions to set. If None, query the current setting. + + UIFLOW2: + + |set_retransmission_count.png| + +.. method:: LoRaWAN868Module.send_hex(hex_data) + + Send hex data in LoRaWAN or LoRa mode. Hex characters must be in pairs (e.g., "AABB"). + + :param hex_data: The hex data to send, up to 64 bytes. + + UIFLOW2: + + |send_hex.png| + +.. method:: LoRaWAN868Module.send_string(string_data) + + Send string data in LoRaWAN or LoRa mode. The string must consist of ASCII characters. + + :param string_data: The string data to send, up to 64 bytes. + + UIFLOW2: + + |send_string.png| + +.. method:: LoRaWAN868Module.query_lorawan_mode() + + Query if the device is in LoRaWAN or normal LoRa mode. + + + UIFLOW2: + + |query_lorawan_mode.png| + +.. method:: LoRaWAN868Module.save_parameters_to_flash() + + Save the current LoRa parameters to FLASH memory. + + + UIFLOW2: + + |save_parameters_to_flash.png| + +.. method:: LoRaWAN868Module.at_cmd(cmd, data) + + Send an AT command to the LoRaWAN module. + + :param cmd: The AT command to send. + :param data: The data to send with the AT command. + + UIFLOW2: + + |at_cmd.png| + +.. method:: LoRaWAN868Module.at_query(cmd) + + Query the current settings of the LoRaWAN module. + + :param cmd: The AT command to query. + + UIFLOW2: + + |at_query.png| + +.. method:: LoRaWAN868Module.at_receive() + + Receive a response from the LoRaWAN module. + + + UIFLOW2: + + |at_receive.png| + +.. method:: LoRaWAN868Module.flush() + + Clear the UART buffer. + + + UIFLOW2: + + |flush.png| + +.. method:: LoRaWAN868Module.any() + + Check if there is any data in the UART buffer. + + + UIFLOW2: + + |any.png| + +.. method:: LoRaWAN868Module.receive_data() + + Receive data from the LoRaWAN module. + + + UIFLOW2: + + |receive_data.png| + + + +Constants +--------- + + .. data:: LoRaWAN868Module.BAND_470 + .. data:: LoRaWAN868Module.BAND_868 + .. data:: LoRaWAN868Module.BAND_915 + + LoRa band frequency + + .. data:: LoRaWAN868Module.MODE_LORA + .. data:: LoRaWAN868Module.MODE_LORAWAN + + LoRa Mode + diff --git a/docs/en/module/nbiot.rst b/docs/en/module/nbiot.rst new file mode 100644 index 00000000..a083a2a1 --- /dev/null +++ b/docs/en/module/nbiot.rst @@ -0,0 +1,30 @@ +NB-IoT Module +============= + +.. include:: ../refs/module.nbiot.ref + +The following products are supported: + + |NB-IoT Module| + + +class NBIOTModule +----------------- + +Constructors +------------ + +.. class:: NBIOTModule(id: Literal[0, 1, 2], tx: int, rx: int) + + Create a NBIOTModule object + + :param id: UART ID. + :param int tx: the UART TX pin. + :param int rx: the UART RX pin. + + UIFLOW2: + + |init.png| + + +See :ref:`unit.NBIOTUnit.Methods ` for more details. diff --git a/docs/en/module/step_motor_driver.rst b/docs/en/module/step_motor_driver.rst new file mode 100644 index 00000000..9762b4b1 --- /dev/null +++ b/docs/en/module/step_motor_driver.rst @@ -0,0 +1,230 @@ + +StepMotorDriverModule +===================== + +.. include:: ../refs/module.stepmotordrivermodule.ref + +StepMotor Driver Module 13.2 V1.1 is a stepper motor driver adapted to M5 main control, using STM32+HR8825 stepper motor drive scheme, providing 3-way bipolar stepper motor control interface. + +Support the following products: + +|StepMotorDriverModule| + +Micropython Example: + + .. literalinclude:: ../../../examples/module/step_motor_driver.py + :language: python + :linenos: + + +UIFLOW2 Example: + + |example.png| + +.. only:: builder_html + + |step_motor_driver.m5f2| + +class StepMotorDriverModule +--------------------------- + +Constructors +------------ + +.. class:: StepMotorDriverModule(address, step_pin, dir_pin) + + Initialize the StepMotorDriverModule. + + :param hex address: The I2C address of the device. + :param tuple step_pin: The step pin (X, Y, Z) of the motor. + :param tuple dir_pin: The dir pin (X, Y, Z) of the motor. + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: StepMotorDriverModule.reset_motor(motor_id, state) + + Reset the motor. + + :param motor_id: The motor to reset. + Options: + - ``X``: StepMotorDriverModule.MOTOR_X + - ``Y``: StepMotorDriverModule.MOTOR_Y + - ``Z``: StepMotorDriverModule.MOTOR_Z + :param bool state: The state of the motor. + + UIFLOW2: + + |reset_motor.png| + +.. method:: StepMotorDriverModule.set_motor_state(state) + + Enable or disable the motor. + + :param bool state: The state of the motor. + + UIFLOW2: + + |set_motor_state.png| + +.. method:: StepMotorDriverModule.set_microstep(step) + + Set the microstep. + + :param step: The microstep value. + Options: + - ``FULL``: StepMotorDriverModule.STEP_FULL + - ``1/2``: StepMotorDriverModule.STEP1_2 + - ``1/4``: StepMotorDriverModule.STEP1_4 + - ``1/8``: StepMotorDriverModule.STEP1_8 + - ``1/16``: StepMotorDriverModule.STEP1_16 + - ``1/32``: StepMotorDriverModule.STEP1_32 + + UIFLOW2: + + |set_microstep.png| + +.. method:: StepMotorDriverModule.set_motor_pwm_freq(motor_id, freq) + + Set the motor pwm freq. + + :param motor_id: The motor to set the freq. + Options: + - ``X``: StepMotorDriverModule.MOTOR_X + - ``Y``: StepMotorDriverModule.MOTOR_Y + - ``Z``: StepMotorDriverModule.MOTOR_Z + :param int freq: The freq value. + + UIFLOW2: + + |set_motor_pwm_freq.png| + +.. method:: StepMotorDriverModule.set_motor_direction(motor_id, direction) + + Set the motor direction. + + :param motor_id: The motor to set the direction. + Options: + - ``X``: StepMotorDriverModule.MOTOR_X + - ``Y``: StepMotorDriverModule.MOTOR_Y + - ``Z``: StepMotorDriverModule.MOTOR_Z + :param bool direction: The direction value. + Options: + - ``Positive``: 1 + - ``Negative``: 0 + + UIFLOW2: + + |set_motor_direction.png| + +.. method:: StepMotorDriverModule.get_all_limit_switch_state() + + Get all io state. + + + UIFLOW2: + + |get_all_limit_switch_state.png| + +.. method:: StepMotorDriverModule.get_limit_switch_state(switch_id) + + Get the io state. + + :param int switch_id: The io id. + + UIFLOW2: + + |get_limit_switch_state.png| + +.. method:: StepMotorDriverModule.get_fault_io_state(motor_id) + + Get the fault io state. + + :param int motor_id: The motor id. + Options: + - ``X``: StepMotorDriverModule.MOTOR_X + - ``Y``: StepMotorDriverModule.MOTOR_Y + - ``Z``: StepMotorDriverModule.MOTOR_Z + + UIFLOW2: + + |get_fault_io_state.png| + +.. method:: StepMotorDriverModule.motor_control(motor_id, state) + + Control the motor to rotate/stop. + + :param motor_id: The motor id. + Options: + - ``X``: StepMotorDriverModule.MOTOR_X + - ``Y``: StepMotorDriverModule.MOTOR_Y + - ``Z``: StepMotorDriverModule.MOTOR_Z + :param bool state: The state value. + Options: + - ``Rotate``: 1 + - ``Stop``: 0 + + UIFLOW2: + + |motor_control.png| + +.. method:: StepMotorDriverModule.get_firmware_version() + + Get the firmware version. + + + UIFLOW2: + + |get_firmware_version.png| + +.. method:: StepMotorDriverModule.set_i2c_address(new_address) + + Set the i2c address. + + :param int new_address: The new address. + + UIFLOW2: + + |set_i2c_address.png| + + + +Constants +--------- + + .. data:: StepMotorDriverModule.MOTOR_X + .. data:: StepMotorDriverModule.MOTOR_Y + .. data:: StepMotorDriverModule.MOTOR_Z + + Motor IDs + + .. data:: StepMotorDriverModule.MOTOR_STATE_ENABLE + .. data:: StepMotorDriverModule.MOTOR_STATE_DISABLE + + Motor states + + .. data:: StepMotorDriverModule.INPUT_REG + .. data:: StepMotorDriverModule.OUTPUT_REG + .. data:: StepMotorDriverModule.POLINV_REG + .. data:: StepMotorDriverModule.CONFIG_REG + .. data:: StepMotorDriverModule.FAULT_REG + .. data:: StepMotorDriverModule.RESET_REG + .. data:: StepMotorDriverModule.FIRM_REG + .. data:: StepMotorDriverModule.I2C_REG + + Register addresses + + .. data:: StepMotorDriverModule.STEP_FULL + .. data:: StepMotorDriverModule.STEP1_2 + .. data:: StepMotorDriverModule.STEP1_4 + .. data:: StepMotorDriverModule.STEP1_8 + .. data:: StepMotorDriverModule.STEP1_16 + .. data:: StepMotorDriverModule.STEP1_32 + + Microstep values + diff --git a/docs/en/refs/module.grbl.ref b/docs/en/refs/module.grbl.ref new file mode 100644 index 00000000..6a68c7e7 --- /dev/null +++ b/docs/en/refs/module.grbl.ref @@ -0,0 +1,33 @@ + +.. |GRBLModule| image:: https://static-cdn.m5stack.com/resource/docs/products/module/grbl13.2/grbl13.2_01.webp + :target: https://docs.m5stack.com/en/module/grbl13.2 + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/init.png +.. |g_code.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/g_code.png +.. |get_code_time.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/get_code_time.png +.. |turn.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/turn.png +.. |set_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/set_mode.png +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/init.png +.. |flush.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/flush.png +.. |get_message.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/get_message.png +.. |get_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/get_status.png +.. |get_idle_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/get_idle_state.png +.. |get_lock_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/get_lock_state.png +.. |wait_idle.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/wait_idle.png +.. |unlock_alarm_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/unlock_alarm_state.png +.. |lock.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/lock.png +.. |unlock.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/unlock.png + +.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/grblmodule/example.png + +.. |grbl_example.m5f2| raw:: html + + + grbl_example.m5f2 + + diff --git a/docs/en/refs/module.lorawan868.ref b/docs/en/refs/module.lorawan868.ref new file mode 100644 index 00000000..1a5c44ff --- /dev/null +++ b/docs/en/refs/module.lorawan868.ref @@ -0,0 +1,62 @@ + +.. |LoRaWAN868Module| image:: https://static-cdn.m5stack.com/resource/docs/products/module/comx_lorawan/comx_lorawan_01.webp + :target: https://docs.m5stack.com/en/module/comx_lorawan + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/init.png +.. |set_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_mode.png +.. |set_parameters.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_parameters.png +.. |wake_up.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/wake_up.png +.. |sleep.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/sleep.png +.. |reset.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/reset.png +.. |restore_factory_settings.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/restore_factory_settings.png +.. |set_copyright.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_copyright.png +.. |set_auto_low_power.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_auto_low_power.png +.. |query_chip_id.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/query_chip_id.png +.. |enable_rx.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/enable_rx.png +.. |set_deveui.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_deveui.png +.. |set_appeui.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_appeui.png +.. |set_appkey.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_appkey.png +.. |set_nwkskey.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_nwkskey.png +.. |set_appskey.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_appskey.png +.. |set_devaddr.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_devaddr.png +.. |set_otaa_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_otaa_mode.png +.. |set_adr.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_adr.png +.. |set_channel_mask.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_channel_mask.png +.. |join_network.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/join_network.png +.. |set_duty_cycle.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_duty_cycle.png +.. |set_class_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_class_mode.png +.. |set_ack.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_ack.png +.. |set_app_port.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_app_port.png +.. |set_retransmission_count.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/set_retransmission_count.png +.. |send_hex.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/send_hex.png +.. |send_string.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/send_string.png +.. |query_lorawan_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/query_lorawan_mode.png +.. |save_parameters_to_flash.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/save_parameters_to_flash.png +.. |at_cmd.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/at_cmd.png +.. |at_query.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/at_query.png +.. |at_receive.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/at_receive.png +.. |flush.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/flush.png +.. |any.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/any.png +.. |receive_data.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/receive_data.png + +.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/lorawan868module/example.png + +.. |lorawan868_example_tx.m5f2| raw:: html + + + lorawan868_example_tx.m5f2 + +.. |lorawan868_example_rx.m5f2| raw:: html + + + lorawan868_example_rx.m5f2 + + diff --git a/docs/en/refs/module.nbiot.ref b/docs/en/refs/module.nbiot.ref new file mode 100644 index 00000000..c359f653 --- /dev/null +++ b/docs/en/refs/module.nbiot.ref @@ -0,0 +1,7 @@ +.. |NB-IoT Module| image:: https://static-cdn.m5stack.com/resource/docs/products/module/comx_nb-iot/comx_nb-iot_01.webp + :target: https://docs.m5stack.com/en/module/comx_nb-iot + :height: 200px + :width: 200px + + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/nbiot/init.png \ No newline at end of file diff --git a/docs/en/refs/module.step_motor_driver.ref b/docs/en/refs/module.step_motor_driver.ref new file mode 100644 index 00000000..1d56c737 --- /dev/null +++ b/docs/en/refs/module.step_motor_driver.ref @@ -0,0 +1,30 @@ + +.. |StepMotorDriverModule| image:: https://static-cdn.m5stack.com/resource/docs/products/module/Stepmotor%20Driver%20Module13.2%20v1.1/img-c2b8ceac-b6be-4cec-9a15-228fcf8623e7.webp + :target: https://docs.m5stack.com/en/module/Stepmotor%20Driver%20Module13.2%20v1.1 + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/init.png +.. |reset_motor.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/reset_motor.png +.. |set_motor_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/set_motor_state.png +.. |set_microstep.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/set_microstep.png +.. |set_motor_pwm_freq.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/set_motor_pwm_freq.png +.. |set_motor_direction.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/set_motor_direction.png +.. |get_all_limit_switch_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/get_all_limit_switch_state.png +.. |get_limit_switch_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/get_limit_switch_state.png +.. |get_fault_io_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/get_fault_io_state.png +.. |motor_control.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/motor_control.png +.. |get_firmware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/get_firmware_version.png +.. |set_i2c_address.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/set_i2c_address.png + +.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/module/stepmotordrivermodule/example.png + +.. |step_motor_driver.m5f2| raw:: html + + + step_motor_driver.m5f2 + + diff --git a/docs/en/refs/unit.roller485.ref b/docs/en/refs/unit.roller485.ref new file mode 100644 index 00000000..26b977a2 --- /dev/null +++ b/docs/en/refs/unit.roller485.ref @@ -0,0 +1,87 @@ + +.. |Roller485| image:: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/products/unit/Unit-Roller485/4.webp + :target: https://docs.m5stack.com/en/unit/Unit-Roller485 + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/init.png +.. |set_motor_output_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_output_state.png +.. |get_motor_output_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_output_state.png +.. |set_motor_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_mode.png +.. |get_motor_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_mode.png +.. |set_motor_over_range_protect_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_over_range_protect_state.png +.. |get_motor_over_range_protect_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_over_range_protect_state.png +.. |remove_motor_jam_protect.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/remove_motor_jam_protect.png +.. |get_motor_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_status.png +.. |get_motor_error_code.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_error_code.png +.. |set_button_change_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_button_change_mode.png +.. |get_button_change_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_button_change_mode.png +.. |set_motor_jam_protect_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_jam_protect_state.png +.. |get_motor_jam_protect_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_jam_protect_state.png +.. |set_motor_id.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_id.png +.. |get_motor_id.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_id.png +.. |set_485_baudrate.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_485_baudrate.png +.. |get_485_baudrate.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_485_baudrate.png +.. |set_rgb_brightness.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_rgb_brightness.png +.. |get_rgb_brightness.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_rgb_brightness.png +.. |set_motor_speed.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_speed.png +.. |get_motor_speed.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_speed.png +.. |set_speed_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_speed_max_current.png +.. |get_speed_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_speed_max_current.png +.. |get_motor_speed_readback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_speed_readback.png +.. |set_motor_speed_pid.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_speed_pid.png +.. |get_motor_speed_pid.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_speed_pid.png +.. |set_motor_position.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_position.png +.. |get_motor_position.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_position.png +.. |set_position_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_position_max_current.png +.. |get_position_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_position_max_current.png +.. |get_motor_position_readback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_position_readback.png +.. |get_motor_position_pid.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_position_pid.png +.. |set_motor_position_pid.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_position_pid.png +.. |set_motor_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_motor_max_current.png +.. |get_motor_max_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_max_current.png +.. |get_motor_current_readback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_motor_current_readback.png +.. |set_rgb_color.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_rgb_color.png +.. |get_rgb_color.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_rgb_color.png +.. |set_rgb_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_rgb_mode.png +.. |get_rgb_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_rgb_mode.png +.. |get_vin_voltage.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_vin_voltage.png +.. |get_temperature_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_temperature_value.png +.. |set_encoder_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_encoder_value.png +.. |get_encoder_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_encoder_value.png +.. |save_param_to_flash.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/save_param_to_flash.png +.. |get_firmware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_firmware_version.png +.. |set_i2c_address.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/set_i2c_address.png +.. |get_i2c_address.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/get_i2c_address.png + +.. |i2c_example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/i2c_example.png +.. |rs485_example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/rs485_example.png +.. |rs485_i2c_example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/roller485/rs485_i2c_example.png + +.. |roller485_i2c_fire_example.m5f2| raw:: html + + + roller485_i2c_fire_example.m5f2 + + +.. |roller485_485_fire_example.m5f2| raw:: html + + + roller485_485_fire_example.m5f2 + + + +.. |roller485_485toi2c_fire_example.m5f2| raw:: html + + + roller485_485toi2c_fire_example.m5f2 + \ No newline at end of file diff --git a/docs/en/refs/unit.timerpwr.ref b/docs/en/refs/unit.timerpwr.ref new file mode 100644 index 00000000..d697e737 --- /dev/null +++ b/docs/en/refs/unit.timerpwr.ref @@ -0,0 +1,44 @@ + +.. |TimerPWRUnit| image:: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/products/unit/Unit-TimerPWR/4.webp + :target: https://docs.m5stack.com/en/unit/Unit-TimerPWR + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/init.png +.. |get_firmware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_firmware_version.png +.. |get_battery_voltage.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_battery_voltage.png +.. |get_battery_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_battery_current.png +.. |get_usb_voltage.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_usb_voltage.png +.. |get_usb_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_usb_current.png +.. |get_grove_voltage.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_grove_voltage.png +.. |get_grove_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_grove_current.png +.. |is_charging.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/is_charging.png +.. |get_button_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_button_status.png +.. |save_data_to_flash.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/save_data_to_flash.png +.. |get_grove_output_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_grove_output_status.png +.. |set_grove_output_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_grove_output_status.png +.. |get_oled_backlight_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/get_oled_backlight_status.png +.. |set_oled_backlight_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_oled_backlight_status.png +.. |sleep_once.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/sleep_once.png +.. |set_power_on_time.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_power_on_time.png +.. |set_power_off_time.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_power_off_time.png +.. |sleep_cycle.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/sleep_cycle.png +.. |set_cycle_sleep.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_cycle_sleep.png +.. |set_wakeup_trigger.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_wakeup_trigger.png +.. |set_sleep_trigger.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_sleep_trigger.png +.. .. |set_callback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/set_callback.png +.. |usb_callback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/usb_callback.png +.. |charging_callback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/charging_callback.png +.. |button_callback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/button_callback.png +.. |tick.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/tick.png + +.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/timerpwr/example.png + +.. |atoms3_timerpwr_example.m5f2| raw:: html + + + atoms3_timerpwr_example.m5f2 + \ No newline at end of file diff --git a/docs/en/refs/unit.uhf_rfid.ref b/docs/en/refs/unit.uhf_rfid.ref new file mode 100644 index 00000000..9ddc4325 --- /dev/null +++ b/docs/en/refs/unit.uhf_rfid.ref @@ -0,0 +1,63 @@ +.. |UHFRFIDUnit| image:: https://static-cdn.m5stack.com/resource/docs/products/unit/uhf_rfid/uhf_rfid_01.webp + :target: https://docs.m5stack.com/en/unit/uhf_rfid + :height: 200px + :width: 200px + +.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/example.png + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/init.png +.. |get_demodulator_mixer.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_demodulator_mixer.png +.. |set_demodulator_mixer.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_demodulator_mixer.png +.. |get_demodulator_amplifier.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_demodulator_amplifier.png +.. |set_demodulator_amplifier.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_demodulator_amplifier.png +.. |get_demodulator_threshold.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_demodulator_threshold.png +.. |set_demodulator_threshold.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_demodulator_threshold.png +.. |get_working_region.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_working_region.png +.. |set_working_region.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_working_region.png +.. |get_working_channel.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_working_channel.png +.. |set_working_channel.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_working_channel.png +.. |insert_working_channel.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/insert_working_channel.png +.. |clear_working_channel.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/clear_working_channel.png +.. |set_automatic_hopping.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_automatic_hopping.png +.. |get_channel_rssi.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_channel_rssi.png +.. |get_blocking_signal_strength.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_blocking_signal_strength.png +.. |get_tx_power.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_tx_power.png +.. |set_tx_power.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_tx_power.png +.. |set_continuous_wave.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_continuous_wave.png +.. |get_manufacturer_id.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_manufacturer_id.png +.. |get_hardware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_hardware_version.png +.. |get_firmware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_firmware_version.png +.. |sleep.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/sleep.png +.. |wake.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/wake.png +.. |set_automatic_sleep_time.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_automatic_sleep_time.png +.. |disable_automatic_sleep.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/disable_automatic_sleep.png +.. |inventory.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/inventory.png +.. |set_select_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_select_mode.png +.. |select.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/select.png +.. |set_access_password.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_access_password.png +.. |set_kill_password.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_kill_password.png +.. |kill.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/kill.png +.. |set_query_param.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_query_param.png +.. |lock_mem_bank.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/lock_mem_bank.png +.. |read_mem_bank.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/read_mem_bank.png +.. |write_mem_bank.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/write_mem_bank.png +.. |get_impinj_monza_qt_sr.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_impinj_monza_qt_sr.png +.. |set_impinj_monza_qt_sr.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_impinj_monza_qt_sr.png +.. |get_impinj_monza_qt_mem.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_impinj_monza_qt_mem.png +.. |set_impinj_monza_qt_mem.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_impinj_monza_qt_mem.png +.. |nxp_eas_alarm.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/nxp_eas_alarm.png +.. |get_nxp_config_word.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/get_nxp_config_word.png +.. |set_nxp_config_word.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/set_nxp_config_word.png +.. |nxp_read_protect.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/nxp_read_protect.png +.. |nxp_read_protect1.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/nxp_read_protect1.png +.. |nxp_change_eas.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/nxp_change_eas.png +.. |nxp_change_eas1.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uhf_rfid/nxp_change_eas1.png + +.. |cores3_uhf_rfid_example.m5f2| raw:: html + + + cores3_uhf_rfid_example.m5f2 + diff --git a/docs/en/refs/unit.uwb.ref b/docs/en/refs/unit.uwb.ref new file mode 100644 index 00000000..3a65b344 --- /dev/null +++ b/docs/en/refs/unit.uwb.ref @@ -0,0 +1,42 @@ + +.. |UWBUnit| image:: https://static-cdn.m5stack.com/resource/docs/products/unit/uwb/uwb_01.webp + :target: https://docs.m5stack.com/en/unit/uwb + :height: 200px + :width: 200px + +.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/init.png +.. |get_distance.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/get_distance.png +.. |get_device_id.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/get_device_id.png +.. |get_device_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/get_device_mode.png +.. |set_device_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/set_device_mode.png +.. |isconnected.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/isconnected.png +.. |get_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/get_version.png +.. |reset.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/reset.png +.. |_extract_text.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/_extract_text.png +.. |set_measurement_interval.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/set_measurement_interval.png +.. |set_measurement.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/set_measurement.png +.. |at_cmd_send.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/at_cmd_send.png +.. |set_callback.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/set_callback.png +.. |update.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/update.png + +.. |example_anchor.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/example_anchor.png + +.. |example_tag.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/uwb/example_tag.png + +.. |core2_uwb_anchor_example.m5f2| raw:: html + + + core2_uwb_anchor_example.m5f2 + + +.. |stickc_plus2_uwb_tag_example.m5f2| raw:: html + + + stickc_plus2_uwb_tag_example.m5f2 + \ No newline at end of file diff --git a/docs/en/units/index.rst b/docs/en/units/index.rst index 1d4f6321..d80cdf18 100644 --- a/docs/en/units/index.rst +++ b/docs/en/units/index.rst @@ -65,13 +65,17 @@ Unit relay.rst relay4.rst rgb.rst + roller485.rst scroll.rst ssr.rst synth.rst thermal.rst + timerpwr.rst tmos.rst tof.rst + uhf_rfid.rst ultrasonic.rst + uwb.rst vibrator.rst watering.rst weight_i2c.rst diff --git a/docs/en/units/nbiot.rst b/docs/en/units/nbiot.rst index 8460f040..087a206b 100644 --- a/docs/en/units/nbiot.rst +++ b/docs/en/units/nbiot.rst @@ -56,6 +56,7 @@ Constructors |init.png| +.. _unit.NBIOTUnit.Methods: Methods ------- diff --git a/docs/en/units/roller485.rst b/docs/en/units/roller485.rst new file mode 100644 index 00000000..30d753e4 --- /dev/null +++ b/docs/en/units/roller485.rst @@ -0,0 +1,745 @@ + +Roller485 +========== + +.. include:: ../refs/unit.roller485.ref + +Support the following products: + +|Roller485| + +Roller485 I2C Example: + + .. literalinclude:: ../../../examples/unit/roller485/roller485_i2c_fire_example.py + :language: python + :linenos: + +Roller485 I2C UIFLOW2 Example: + + |i2c_example.png| + +.. only:: builder_html + + |roller485_i2c_fire_example.m5f2| + +Roller485 RS485 Example: + + .. literalinclude:: ../../../examples/unit/roller485/roller485_485_fire_example.py + :language: python + :linenos: + +Roller485 RS485 UIFLOW2 Example: + + |rs485_example.png| + +.. only:: builder_html + + |roller485_485_fire_example.m5f2| + +Roller485 RS485ToI2C Example: + + .. literalinclude:: ../../../examples/unit/roller485/roller485_485toi2c_fire_example.py + :language: python + :linenos: + +Roller485 RS485ToI2C UIFLOW2 Example: + + |rs485_i2c_example.png| + +.. only:: builder_html + + |roller485_485toi2c_fire_example.m5f2| + + +class Roller485Unit +------------------- + +Constructors +------------ + +.. class:: Roller485Unit(bus, address, mode) + + Initialize the Roller485Unit object based on communication mode. + + :param bus: The I2C/RS485 bus instance. + :param address: The motor's RS485 address. Defaults to _ROLLER485_RS485_ADDR. + :param mode: The Roller485 communication mode. + + UIFLOW2: + + |init.png| + +class RollerBase +---------------- + +Constructors +------------ + +.. class:: RollerBase() + + +Methods +------- + +.. method:: RollerBase.set_motor_output_state(ctrl) -> None + + Set the motor output state. + + :param int ctrl: Control value for the motor output. + + UIFLOW2: + + |set_motor_output_state.png| + +.. method:: RollerBase.get_motor_output_state() -> bool + + Get the motor output status. + + :return: True if the motor output is active, False otherwise. + + UIFLOW2: + + |get_motor_output_state.png| + +.. method:: RollerBase.set_motor_mode(mode) -> None + + Set the motor mode. + + :param int mode: The mode to set for the motor. + + UIFLOW2: + + |set_motor_mode.png| + +.. method:: RollerBase.get_motor_mode() -> int + + Get the motor mode. + + :return: The current motor mode. + + UIFLOW2: + + |get_motor_mode.png| + +.. method:: RollerBase.set_motor_over_range_protect_state(state) -> None + + Set the motor over range protection state. + + :param int state: Protection state value (1 to enable, 0 to disable). + + UIFLOW2: + + |set_motor_over_range_protect_state.png| + +.. method:: RollerBase.get_motor_over_range_protect_state() -> bool + + Get the motor over range protection status. + + :return: True if protection is enabled, False otherwise. + + UIFLOW2: + + |get_motor_over_range_protect_state.png| + +.. method:: RollerBase.remove_motor_jam_protect() -> None + + Set the motor jam release protection. + + + UIFLOW2: + + |remove_motor_jam_protect.png| + +.. method:: RollerBase.get_motor_status() -> int + + Get the motor status. + + :return: The current status of the motor. + + UIFLOW2: + + |get_motor_status.png| + +.. method:: RollerBase.get_motor_error_code() -> int + + Get the motor error code. + + :return: The current error code of the motor. + + UIFLOW2: + + |get_motor_error_code.png| + +.. method:: RollerBase.set_button_change_mode(state) -> None + + Set the button change mode. + + :param int state: Change mode state value (1 to enable, 0 to disable). + + UIFLOW2: + + |set_button_change_mode.png| + +.. method:: RollerBase.get_button_change_mode() -> int + + Get the button change mode. + + :return: The current button change mode value. + + UIFLOW2: + + |get_button_change_mode.png| + +.. method:: RollerBase.set_motor_jam_protect_state(state) -> None + + Set the motor jam protection enable/disable. + + :param int state: Protection state value (1 to enable, 0 to disable). + + UIFLOW2: + + |set_motor_jam_protect_state.png| + +.. method:: RollerBase.get_motor_jam_protect_state() -> bool + + Get the motor jam protection status. + + :return: True if jam protection is enabled, False otherwise. + + UIFLOW2: + + |get_motor_jam_protect_state.png| + +.. method:: RollerBase.set_motor_id(id) -> None + + Set the motor ID. + + :param int id: The ID to assign to the motor. + + UIFLOW2: + + |set_motor_id.png| + +.. method:: RollerBase.get_motor_id() -> int + + Get the motor ID. + + :return: The current motor ID. + + UIFLOW2: + + |get_motor_id.png| + +.. method:: RollerBase.set_485_baudrate(bps) -> None + + Set the 485 baudrate. + + :param int bps: Baud rate value. + + UIFLOW2: + + |set_485_baudrate.png| + +.. method:: RollerBase.get_485_baudrate() -> int + + Get the 485 baudrate. + + :return: The current 485 baudrate. + + UIFLOW2: + + |get_485_baudrate.png| + +.. method:: RollerBase.set_rgb_brightness(bright) -> None + + Set RGB brightness. + + :param int bright: Brightness value. + + UIFLOW2: + + |set_rgb_brightness.png| + +.. method:: RollerBase.get_rgb_brightness() -> int + + Get RGB brightness. + + :return: The current RGB brightness value. + + UIFLOW2: + + |get_rgb_brightness.png| + +.. method:: RollerBase.set_motor_speed(speed) -> None + + Set the motor speed and max current setting. + + :param int speed: The speed value to set. + + UIFLOW2: + + |set_motor_speed.png| + +.. method:: RollerBase.get_motor_speed() -> int + + Get the motor speed and max current setting. + + :return: The current motor speed. + + UIFLOW2: + + |get_motor_speed.png| + +.. method:: RollerBase.set_speed_max_current(current) -> None + + Set the motor speed and max current setting. + + :param int current: The max current value to set. + + UIFLOW2: + + |set_speed_max_current.png| + +.. method:: RollerBase.get_speed_max_current() -> int + + Get the motor speed and max current setting. + + :return: The current max current setting. + + UIFLOW2: + + |get_speed_max_current.png| + +.. method:: RollerBase.get_motor_speed_readback() -> float + + Get the motor speed readback. + + :return: The readback value of the motor speed. + + UIFLOW2: + + |get_motor_speed_readback.png| + +.. method:: RollerBase.set_motor_speed_pid(p, i, d) -> None + + Set the motor speed PID. + + :param float p: Proportional gain. + :param float i: Integral gain. + :param float d: Derivative gain. + + UIFLOW2: + + |set_motor_speed_pid.png| + +.. method:: RollerBase.get_motor_speed_pid() -> tuple + + Get the motor speed PID. + + :return: A tuple containing the PID values. + + UIFLOW2: + + |get_motor_speed_pid.png| + +.. method:: RollerBase.set_motor_position(position) -> None + + Set the motor position and max current setting. + + :param int position: The position value to set. + + UIFLOW2: + + |set_motor_position.png| + +.. method:: RollerBase.get_motor_position() -> int + + Get the motor position and max current setting. + + :return: The current motor position. + + UIFLOW2: + + |get_motor_position.png| + +.. method:: RollerBase.set_position_max_current(current) -> None + + Set the motor position and max current setting. + + :param int current: The max current value to set. + + UIFLOW2: + + |set_position_max_current.png| + +.. method:: RollerBase.get_position_max_current() -> int + + Get the motor position and max current setting. + + :return: The current max current setting. + + UIFLOW2: + + |get_position_max_current.png| + +.. method:: RollerBase.get_motor_position_readback() -> float + + Get the motor position readback. + + :return: The readback value of the motor position. + + UIFLOW2: + + |get_motor_position_readback.png| + +.. method:: RollerBase.get_motor_position_pid() -> tuple + + Get the motor position PID. + + :return: A tuple containing the PID values for position. + + UIFLOW2: + + |get_motor_position_pid.png| + +.. method:: RollerBase.set_motor_position_pid(p, i, d) -> None + + Set the motor position PID. + + :param float p: Proportional gain. + :param float i: Integral gain. + :param float d: Derivative gain. + + UIFLOW2: + + |set_motor_position_pid.png| + +.. method:: RollerBase.set_motor_max_current(current) -> None + + Set the motor max current. + + :param int current: The maximum current for the motor, multiplied by 100 before sending. + + UIFLOW2: + + |set_motor_max_current.png| + +.. method:: RollerBase.get_motor_max_current() -> int + + Get the motor max current. + + :return: The motor max current, divided by 100 after reading. + + UIFLOW2: + + |get_motor_max_current.png| + +.. method:: RollerBase.get_motor_current_readback() -> float + + Get the motor current readback. + + :return: The motor current readback value, divided by 100 after reading. + + UIFLOW2: + + |get_motor_current_readback.png| + +.. method:: RollerBase.set_rgb_color(rgb) -> None + + Set the system RGB color. + + :param int rgb: The RGB color value, where the format is 0xRRGGBB. + + UIFLOW2: + + |set_rgb_color.png| + +.. method:: RollerBase.get_rgb_color() -> tuple + + Get the system RGB color. + + :return: The RGB color as a tuple (R, G, B). + + UIFLOW2: + + |get_rgb_color.png| + +.. method:: RollerBase.set_rgb_mode(mode) -> None + + Set the system RGB mode. + + :param int mode: The RGB mode value. + + UIFLOW2: + + |set_rgb_mode.png| + +.. method:: RollerBase.get_rgb_mode() -> int + + Get the system RGB mode. + + :return: The current RGB mode value. + + UIFLOW2: + + |get_rgb_mode.png| + +.. method:: RollerBase.get_vin_voltage() -> int + + Get the system VIN voltage. + + :return: The system VIN voltage value, multiplied by 10 after reading. + + UIFLOW2: + + |get_vin_voltage.png| + +.. method:: RollerBase.get_temperature_value() -> int + + Get the system temperature. + + :return: The current system temperature value. + + UIFLOW2: + + |get_temperature_value.png| + +.. method:: RollerBase.set_encoder_value(count) -> None + + Set the system encoder value. + + :param int count: The encoder count value. + + UIFLOW2: + + |set_encoder_value.png| + +.. method:: RollerBase.get_encoder_value() -> int + + Get the system encoder value. + + :return: The current encoder value. + + UIFLOW2: + + |get_encoder_value.png| + +.. method:: RollerBase.save_param_to_flash() -> None + + Save the motor data to flash. + + + UIFLOW2: + + |save_param_to_flash.png| + +.. method:: RollerBase.get_firmware_version() -> int + + Get the device firmware version. + + :return: The current firmware version. + + UIFLOW2: + + |get_firmware_version.png| + +.. method:: RollerBase.set_i2c_address(addr) -> None + + Set the I2C address. + + :param int addr: The new I2C address. Must be between 0x08 and 0x77. + + UIFLOW2: + + |set_i2c_address.png| + +.. method:: RollerBase.get_i2c_address() -> int + + Get the current I2C address. + + :return: The current I2C address. + + UIFLOW2: + + |get_i2c_address.png| + + +class RollerI2C(RollerBase) +--------------------------- + +Constructors +------------ + +.. class:: RollerI2C(i2c, address) + + Initialize the RollerI2C object. + + :param I2C|PAHUBUnit i2c: I2C bus instance or PAHUBUnit instance. + :param int address: I2C address of the device. Defaults to _ROLLER485_I2C_ADDR. + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: RollerI2C.read(register, length) -> bytes + + Read data from a specified register on the I2C device. + + :param register: The name of the register to read from. + :param length: The number of bytes to read. + :return: The data read from the device as a bytes object. + +.. method:: RollerI2C.write(register, bytes) -> None + + Write data to a specified register on the I2C device. + + :param register: The name of the register to write to. + :param bytes: The data to write to the register as a bytes object. + + +class Roller485(RollerBase) +---------------------------- + +Constructors +------------ + +.. class:: Roller485(bus, address) + + Initialize the Roller485 object. + + :param bus: The RS485 bus instance. + :param int address: The motor's RS485 address. Defaults to _ROLLER485_RS485_ADDR. + +Methods +------- + +.. method:: Roller485.read(register, length) -> bytes + + Read data from a specified register via RS485. + + :param register: The name of the register to read from. + :param length: The number of bytes to read. + + +.. method:: Roller485.create_frame(cmd, motor_id, *datas) -> None + + Create a command frame with the given command and motor ID. + + :param cmd: The command byte. + :param motor_id: The ID of the motor. + :param datas: Additional data bytes to include in the frame. + +.. method:: Roller485.write(register, bytes) -> bool + + Write data to a specified register via RS485. + + :param register: The name of the register to write to. + :param bytes: The data to write to the register as a bytes object. + :return: The response after writing the data. + +.. method:: Roller485.send_command(cmd, id, data, buf_len) -> bool + + Send a command via RS485. + + :param cmd: The command byte. + :param id: The motor ID. + :param data: The data to send along with the command. + :param buf_len: The length of the buffer. + + +.. method:: Roller485.read_response(cmd, id) -> tuple[Literal[True], Any] + + Send a command via RS485. + + :param cmd: The command byte. + :param id: The motor ID. + :return: A tuple (success, response). Success is True if the response is valid, and response is the data read. + +.. method:: Roller485._crc8(buffer) -> int + + Calculate CRC8 checksum. + + :param buffer: The data buffer to compute the checksum for. + :return: The computed CRC8 value. + + +class Roller485ToI2CBus(RollerBase) +----------------------------------- + +Constructors +------------ + +.. class:: Roller485ToI2CBus(bus, address, mode) + + Initialize the Roller485ToI2CBus object. + + :param bus: The RS485 bus instance. + :param address: The motor's RS485 address. Defaults to _ROLLER485_RS485_ADDR. + +Methods +------- + +.. method:: Roller485ToI2CBus.readfrom_mem(addr, mem_addr, nbytes) -> bytes + + Read data from a specific register of the I2C slave device. + + :param addr: The I2C slave address to read from. + :param mem_addr: Memory register address. + :param nbytes: The number of bytes to read. + :return: The data read from the register. + :throws Exception: If the read operation fails. + +.. method:: Roller485ToI2CBus.readfrom_mem_into(addr, mem_addr, buf) + + Read data from a specific register of the I2C slave device. + + :param addr: The I2C slave address to read from. + :param mem_addr: Memory register address. + :param buf: Buffer to store the data. + +.. method:: Roller485ToI2CBus.writeto_mem(addr, mem_addr, buf) -> Literal[True] + + Write data to a specific register of the I2C slave device. + + :param addr: The I2C slave address to write to. + :param mem_addr: Memory register address. + :param buf: The data bytes to write. + :return: True if the write operation is successful. + :throws Exception: If the write operation fails. + +.. method:: Roller485ToI2CBus.readfrom(addr, nbytes) -> bytes + + Read data from the I2C slave device via RS485. + + :param addr: The I2C slave address to read from. + :param nbytes: The number of bytes to read. + :return: The data read from the I2C slave. + :throws Exception: If the read operation fails. + +.. method:: Roller485ToI2CBus.readfrom_into(addr, buf) + + Read data from the I2C slave device via RS485. + + :param addr: I2C device address. + :param buf: Buffer to store the data. + +.. method:: Roller485ToI2CBus.writeto(addr, buf, stop) -> Literal[True] + + Write data to the I2C slave device via RS485. + + :param addr: The I2C slave address to write to. + :param buf: The data bytes to write. + :param stop: Whether to send a stop bit after writing. + :return: True if the write operation is successful. + :throws Exception: If the write operation fails. + + +.. method:: Roller485ToI2CBus.scan(addr, buf, stop) -> List + + Scan for I2C devices on the bus. + + :return: A list of addresses of the found I2C devices. \ No newline at end of file diff --git a/docs/en/units/timerpwr.rst b/docs/en/units/timerpwr.rst new file mode 100644 index 00000000..2bcc0604 --- /dev/null +++ b/docs/en/units/timerpwr.rst @@ -0,0 +1,382 @@ + +TimerPWR Unit +============= + +.. include:: ../refs/unit.timerpwr.ref + +The TimerPWR Unit is a timed power supply unit whose main functions are "charging & discharging + timed switching + screen display + boost output." It features an internal STM32 microcontroller that implements RTC and overall control, allowing users to set automatic power on/off times as needed. It is powered via the Type-C interface and can be connected to an external rechargeable battery via a 1.25-2P interface. The unit includes a built-in battery charging circuit supporting a charging current of 330mA. It also features an integrated DCDC boost circuit that provides a 5V/800mA (1400mA @ 1C battery power) power output to external devices via the Grove port. Additionally, the INA3221 sensor is built-in, allowing real-time monitoring of power input and output current and voltage. The unit is equipped with a 0.66-inch OLED display and two side buttons for user interaction, making it easy to view real-time system status and modify settings. Users can set parameters such as power on/off using the side buttons or via the I2C bus through the Grove interface with I2C commands. This product is suitable for smart homes, industrial automation, and timed control devices. + +Support the following products: + +|TimerPWRUnit| + +Micropython Example: + + .. literalinclude:: ../../../examples/unit/timerpwr/atoms3_timerpwr_example.py + :language: python + :linenos: + + +UIFLOW2 Example: + + |example.png| + +.. only:: builder_html + + |atoms3_timerpwr_example.m5f2| + +class TimerPWRUnit +------------------ + +Constructors +------------ + +.. class:: TimerPWRUnit(i2c, address) + + Create a TimerPWR object. + + :param i2c: I2C object + :param int address: I2C address, 0x56 by default + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: TimerPWRUnit.get_firmware_version() + + Get firmware version. + + :return (int): Firmware version. + + UIFLOW2: + + |get_firmware_version.png| + +.. method:: TimerPWRUnit.get_battery_voltage() + + Get battery voltage. + + :return (int): Battery voltage, in millivolt. + + UIFLOW2: + + |get_battery_voltage.png| + +.. method:: TimerPWRUnit.get_battery_current() + + Get battery current. + + :return (int): Battery current, in milliamperes. + + UIFLOW2: + + |get_battery_current.png| + +.. method:: TimerPWRUnit.get_usb_voltage() + + Get USB voltage. + + :return (int): USB voltage, in millivolt. + + UIFLOW2: + + |get_usb_voltage.png| + +.. method:: TimerPWRUnit.get_usb_current() + + Get USB current. + + :return (int): USB current, in milliamperes. + + UIFLOW2: + + |get_usb_current.png| + +.. method:: TimerPWRUnit.get_grove_voltage() + + Get Grove voltage. + + :return (int): Grove voltage, in millivolt. + + UIFLOW2: + + |get_grove_voltage.png| + +.. method:: TimerPWRUnit.get_grove_current() + + Get Grove current. + + :return (int): Grove current, in milliamperes. + + UIFLOW2: + + |get_grove_current.png| + +.. method:: TimerPWRUnit.is_charging() + + Check if the battery is charging. + + :return (bool): True if charging, False if not. + + UIFLOW2: + + |is_charging.png| + +.. method:: TimerPWRUnit.get_button_status(btn) + + Get button status. + + :param int btn: button index. + + Options: + - ``A``: 0 + - ``B``: 1 + + :return (bool): True if pressed, False if not. + + UIFLOW2: + + |get_button_status.png| + +.. method:: TimerPWRUnit.save_data_to_flash() + + Save data to flash. + + + UIFLOW2: + + |save_data_to_flash.png| + +.. method:: TimerPWRUnit.get_grove_output_status() + + Get Grove output status + + :return (bool): True if enabled, False if disabled. + + UIFLOW2: + + |get_grove_output_status.png| + +.. method:: TimerPWRUnit.set_grove_output_status(enable) + + Set Grove output status. + + :param bool enable: Enable or disable Grove output. + + Options: + - ``Enable``: True + - ``Disable``: False + + UIFLOW2: + + |set_grove_output_status.png| + +.. method:: TimerPWRUnit.get_oled_backlight_status() + + Get OLED backlight status. + + :return (bool): True if enabled, False if disabled. + + UIFLOW2: + + |get_oled_backlight_status.png| + +.. method:: TimerPWRUnit.set_oled_backlight_status(enable) + + Set OLED backlight status. + + :param bool enable: Enable or disable OLED backlight. + + Options: + - ``Enable``: True + - ``Disable``: False + + UIFLOW2: + + |set_oled_backlight_status.png| + +.. method:: TimerPWRUnit.sleep_once(whours, wmintues, wseconds, shours, smintues, sseconds) + + Set sleep once after hours, mintues, seconds and wake up in hours, mintues, seconds. + + :param int whours: Hours to wait before sleep. + :param int wmintues: Mintues to wait before sleep. + :param int wseconds: Seconds to wait before sleep. + :param int shours: Hours to wait before wake up. + :param int smintues: Mintues to wait before wake up. + :param int sseconds: Seconds to wait before wake up. + + UIFLOW2: + + |sleep_once.png| + +.. method:: TimerPWRUnit.set_power_on_time(hours, mintues, seconds) + + Set power on time. + + :param int hours: Hours to power on. + :param int mintues: Mintues to power on. + :param int seconds: Seconds to power on. + +.. method:: TimerPWRUnit.set_power_off_time(hours, mintues, seconds) + + Set power off time. + + :param int hours: Hours to power off. + :param int mintues: Mintues to power off. + :param int seconds: Seconds to power off. + +.. method:: TimerPWRUnit.sleep_cycle(whours, wmintues, wseconds, shours, smintues, sseconds) + + Set sleep cycle after hours, mintues, seconds and wake up in hours, mintues, seconds. + + :param int whours: Hours to wait before sleep. + :param int wmintues: Mintues to wait before sleep. + :param int wseconds: Seconds to wait before sleep. + :param int shours: Hours to wait before wake up. + :param int smintues: Mintues to wait before wake up. + :param int sseconds: Seconds to wait before wake up. + + UIFLOW2: + + |sleep_cycle.png| + +.. method:: TimerPWRUnit.set_cycle_sleep(enable) + + Set cycle sleep. + + :param bool enable: Enable or disable cycle sleep. + + Options: + - ``Enable``: True + - ``Disable``: False + +.. method:: TimerPWRUnit.set_wakeup_trigger(trigger) + + Set wake-up trigger. + + :param trigger: Set wake-up trigger. + + Options: + - ``ALL``: timerpwrunit_0.TRIG_ALL + - ``TIMER``: timerpwrunit_0.TRIG_TIMER + - ``BUTTON``: timerpwrunit_0.TRIG_BUTTON + - ``NONE``: timerpwrunit_0.TRIG_NONE + + UIFLOW2: + + |set_wakeup_trigger.png| + +.. method:: TimerPWRUnit.set_sleep_trigger(trigger) + + Set sleep trigger. + + :param trigger: Set sleep trigger. + + Options: + - ``ALL``: timerpwrunit_0.TRIG_ALL + - ``TIMER``: timerpwrunit_0.TRIG_TIMER + - ``BUTTON``: timerpwrunit_0.TRIG_BUTTON + - ``I2C``: timerpwrunit_0.TRIG_I2C + - ``NONE``: timerpwrunit_0.TRIG_NONE + + UIFLOW2: + + |set_sleep_trigger.png| + +.. method:: TimerPWRUnit.set_callback(event, callback) + + Set callback function. + + :param event: event type. + + Options: + - ``USB inserted``: timerpwrunit_0.EVENT_USB_INSERTED + - ``USB removed``: timerpwrunit_0.EVENT_USB_REMOVED + - ``Button A pressed``: timerpwrunit_0.EVENT_BUTTONA_PRESSED + - ``Button A released``: timerpwrunit_0.EVENT_BUTTONA_RELEASED + - ``Button B pressed``: timerpwrunit_0.EVENT_BUTTONB_PRESSED + - ``Button B released``: timerpwrunit_0.EVENT_BUTTONB_RELEASED + - ``Not charging``: timerpwrunit_0.EVENT_NOT_CHARGING + - ``Charging``: timerpwrunit_0.EVENT_CHARGING + + :param callback: callback function. + + UIFLOW2: + + |usb_callback.png| + + |charging_callback.png| + + |button_callback.png| + +.. method:: TimerPWRUnit.tick() + + Update status in loop. + + + UIFLOW2: + + |tick.png| + + + +Constants +--------- + +.. data:: TimerPWRUnit._SLEEP_COMMAND_REG +.. data:: TimerPWRUnit._CYCLE_REG +.. data:: TimerPWRUnit._GROVE_OUTPUT_REG +.. data:: TimerPWRUnit._OLED_BACKLIGHT_REG +.. data:: TimerPWRUnit._WAKE_UP_TRIGGER_REG +.. data:: TimerPWRUnit._SLEEP_TRIGGER_REG +.. data:: TimerPWRUnit._POWER_ON_TIME_REG +.. data:: TimerPWRUnit._POWER_OFF_TIME_REG +.. data:: TimerPWRUnit._BUTTON_STATUS_REG +.. data:: TimerPWRUnit._USB_VOLTAGE_REG +.. data:: TimerPWRUnit._USB_CURRENT_REG +.. data:: TimerPWRUnit._GROVE_VOLTAGE_REG +.. data:: TimerPWRUnit._GROVE_CURRENT_REG +.. data:: TimerPWRUnit._BATTERY_VOLTAGE_REG +.. data:: TimerPWRUnit._BATTERY_CURRENT_REG +.. data:: TimerPWRUnit._CHARGING_STATUS_REG +.. data:: TimerPWRUnit._SAVE_DATA_TO_FLASH_REG +.. data:: TimerPWRUnit._FW_VERSION_REG +.. data:: TimerPWRUnit._I2C_ADDRESS_REG + + register address. + + +.. data:: TimerPWRUnit.TRIG_ALL +.. data:: TimerPWRUnit.TRIG_TIMER +.. data:: TimerPWRUnit.TRIG_BUTTON +.. data:: TimerPWRUnit.TRIG_I2C +.. data:: TimerPWRUnit.TRIG_NONE + + trigger type. + + +.. data:: TimerPWRUnit.EVENT_USB_INSERTED +.. data:: TimerPWRUnit.EVENT_USB_REMOVED +.. data:: TimerPWRUnit.EVENT_BUTTONA_RELEASED +.. data:: TimerPWRUnit.EVENT_BUTTONA_PRESSED +.. data:: TimerPWRUnit.EVENT_BUTTONB_RELEASED +.. data:: TimerPWRUnit.EVENT_BUTTONB_PRESSED +.. data:: TimerPWRUnit.EVENT_NOT_CHARGING +.. data:: TimerPWRUnit.EVENT_CHARGING + + event type. + + +.. data:: TimerPWRUnit._USB +.. data:: TimerPWRUnit._BUTTON_A +.. data:: TimerPWRUnit._BUTTON_B +.. data:: TimerPWRUnit._CHARGING + + index. + + diff --git a/docs/en/units/uhf_rfid.rst b/docs/en/units/uhf_rfid.rst new file mode 100644 index 00000000..0c579c75 --- /dev/null +++ b/docs/en/units/uhf_rfid.rst @@ -0,0 +1,743 @@ +UHF-RFID Unit +============= + +.. include:: ../refs/unit.uhf_rfid.ref + +Support the following products: + + |UHFRFIDUnit| + + +Micropython Example: + + .. literalinclude:: ../../../examples/unit/uhf_rfid/cores3_uhf_rfid_example.py + :language: python + :linenos: + + +UIFLOW2 Example: + + |example.png| + + +.. only:: builder_html + + |cores3_uhf_rfid_example.m5f2| + + +class UHFRFIDUnit +----------------- + +Constructors +------------ + +.. class:: UHFRFIDUnit(id: Literal[0, 1, 2], port: list | tuple, verbose: bool = False) + + Create a UHF-RFID unit. + + :param int id: The ID of the unit. + :param list|tuple port: The port that the unit is connected to. + :param bool verbose: Print the log information. Default is True. + + UIFLOW2: + + |init.png| + + +Methods +------- + +Demodulator +^^^^^^^^^^^^ + +.. method:: UHFRFIDUnit.get_demodulator_mixer() -> int + + Get demodulator mixer value. + + :return int: demodulator mixer value. + + Options: + - 0x00: 0dB + - 0x01: 3dB + - 0x02: 6dB + - 0x03: 9dB + - 0x04: 12dB + - 0x05: 15dB + - 0x06: 16dB + + UIFLOW2: + + |get_demodulator_mixer.png| + + +.. method:: UHFRFIDUnit.set_demodulator_mixer(value: int) -> bool + + Set demodulator mixer value. + + :param int value: demodulator mixer value. + + Options: + - 0x00: 0dB + - 0x01: 3dB + - 0x02: 6dB + - 0x03: 9dB + - 0x04: 12dB + - 0x05: 15dB + - 0x06: 16dB + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_demodulator_mixer.png| + + +.. method:: UHFRFIDUnit.get_demodulator_amplifier() -> int + + Get demodulator amplifier value. + + :return int: demodulator amplifier value. + + Options: + - 0x00: 12dB + - 0x01: 18dB + - 0x02: 21dB + - 0x03: 24dB + - 0x04: 27dB + - 0x05: 30dB + - 0x06: 36dB + - 0x07: 40dB + + UIFLOW2: + + |get_demodulator_amplifier.png| + + +.. method:: UHFRFIDUnit.set_demodulator_amplifier(value: int) -> bool + + Set demodulator amplifier value. + + :param int value: demodulator amplifier value. + + Options: + - 0x00: 12dB + - 0x01: 18dB + - 0x02: 21dB + - 0x03: 24dB + - 0x04: 27dB + - 0x05: 30dB + - 0x06: 36dB + - 0x07: 40dB + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_demodulator_amplifier.png| + + +.. method:: UHFRFIDUnit.get_demodulator_threshold() -> int + + Get demodulator threshold value. + + :return int: demodulator threshold value. the range is from 0x01B0 to 0xFFFF. + + UIFLOW2: + + |get_demodulator_threshold.png| + + +.. method:: UHFRFIDUnit.set_demodulator_threshold(value: int) -> bool + + Set demodulator threshold value. + + :param int value: demodulator threshold value. the range is from 0x01B0 to 0xFFFF. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_demodulator_threshold.png| + + +Working +^^^^^^^ + +.. method:: UHFRFIDUnit.get_working_region() -> int + + Get work region. + + :return int: work region. + + Options: + - UHFRFIDUnit.CN_900MHZ: China 900MHz + - UHFRFIDUnit.CN_800MHZ: China 800MHz + - UHFRFIDUnit.USA: USA + - UHFRFIDUnit.EUR: EUR + - UHFRFIDUnit.KR: KR + + UIFLOW2: + + |get_working_region.png| + + +.. method:: UHFRFIDUnit.set_working_region(region: int) -> bool + + Set work region. + + :param int region: work region. + + Options: + - UHFRFIDUnit.CN_900MHZ: China 900MHz + - UHFRFIDUnit.CN_800MHZ: China 800MHz + - UHFRFIDUnit.USA: USA + - UHFRFIDUnit.EUR: EUR + - UHFRFIDUnit.KR: KR + + :return int: True if success, False if failed. + + UIFLOW2: + + |set_working_region.png| + + +.. method:: UHFRFIDUnit.get_working_channel() -> int + + Get work channel. + + :return int: work channel. the range is from 0 to 19. + + UIFLOW2: + + |get_working_channel.png| + + +.. method:: UHFRFIDUnit.set_working_channel(channel: int) -> bool + + Set work channel. + + :param int channel: work channel. the range is from 0 to 19. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_working_channel.png| + + +.. method:: UHFRFIDUnit.insert_working_channel(channel: int) -> bool + + Insert work channel. + + :param int channel: work channel. the range is from 0 to 19. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |insert_working_channel.png| + + +.. method:: UHFRFIDUnit.clear_working_channel() -> bool + + Clear work channel. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |clear_working_channel.png| + + +.. method:: UHFRFIDUnit.set_automatic_hopping(enable: bool) -> bool + + Set automatic hopping. + + :param bool enable: enable automatic hopping. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_automatic_hopping.png| + + +RF Power +^^^^^^^^ + +.. method:: UHFRFIDUnit.get_channel_rssi(channel: int) -> int + + Get channel RSSI value. + + :param int channel: work channel. the range is from 0 to 19. + + :return int: channel RSSI value. the unit is dBm. + + UIFLOW2: + + |get_channel_rssi.png| + + +.. method:: UHFRFIDUnit.get_blocking_signal_strength(channel: int) -> int + + Get blocking signal strength. + + :param int channel: work channel. the range is from 0 to 19. + + :return int: blocking signal strength. the unit is dBm. + + UIFLOW2: + + |get_blocking_signal_strength.png| + + +.. method:: UHFRFIDUnit.get_tx_power() -> int + + Get TX power. + + :return int: TX power. the unit is dBm. the range is from -7dBm to 30dBm. + + UIFLOW2: + + |get_tx_power.png| + + +.. method:: UHFRFIDUnit.set_tx_power(power: int) -> bool + + Set TX power. + + :param int power: TX power. the range is from -7dBm to 30dBm. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_tx_power.png| + + +.. method:: UHFRFIDUnit.set_continuous_wave(enable: bool) -> bool + + Set continuous wave. + + :param bool enable: enable continuous wave. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_continuous_wave.png| + + +Module Information and Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. method:: UHFRFIDUnit.get_manufacturer_id() -> str + + Get manufacturer ID. + + :return str: manufacturer ID. + + UIFLOW2: + + |get_manufacturer_id.png| + + +.. method:: UHFRFIDUnit.get_hardware_version() -> str + + Get hardware version. + + :return str: hardware version. + + UIFLOW2: + + |get_hardware_version.png| + + +.. method:: UHFRFIDUnit.get_firmware_version() -> str + + Get firmware version. + + :return str: firmware version. + + UIFLOW2: + + |get_firmware_version.png| + + +.. method:: UHFRFIDUnit.sleep() -> bool + + Set sleep. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |sleep.png| + + +.. method:: UHFRFIDUnit.wake() -> bool + + Set wake up. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |wake.png| + + +.. method:: UHFRFIDUnit.set_automatic_sleep_time(min: int) -> bool + + Set automatic sleep time. + + :param int min: automatic sleep time in minutes. the range is from 1 to 30. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_automatic_sleep_time.png| + + +.. method:: UHFRFIDUnit.disable_automatic_sleep() -> bool + + Disable automatic sleep. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |disable_automatic_sleep.png| + + +Read and Write Tag +^^^^^^^^^^^^^^^^^^ + +.. method:: UHFRFIDUnit.inventory() -> str + + Get tag epc code. if no tag is found, return empty string. + + :return: hexadecimal string of tag epc code. + + UIFLOW2: + + |inventory.png| + + +.. method:: UHFRFIDUnit.set_select_mode(mode: int) -> bool + + Set select mode. + + :param int mode: select mode. + + Options: + - 0x00: need select command + - 0x01: no need select command + - 0x02: part operation need select command + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_select_mode.png| + + +.. method:: UHFRFIDUnit.select(target: int, action: int, membank: int, pointer: int, truncate: bool, mask: str) -> bool + + Set select tag. + + :param int target: target. + :param int action: action. + :param int membank: memory bank. + :param int pointer: pointer. + :param bool truncate: truncate. + :param str mask: EPC code. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |select.png| + + +.. method:: UHFRFIDUnit.set_access_password(old_password: str, new_password: str) -> None + + Set access password. + + :param str old_password: old access password. hexadecimal string. + :param str new_password: new access password. hexadecimal string. + + UIFLOW2: + + |set_access_password.png| + + +.. method:: UHFRFIDUnit.set_kill_password(password) -> None + + Set kill password. + + :param str password: kill password. hexadecimal string. + + UIFLOW2: + + |set_kill_password.png| + + +.. method:: UHFRFIDUnit.kill(password: str) -> bool + + Kill tag. + + :param str password: kill password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |kill.png| + +.. method:: UHFRFIDUnit.set_query_param(dr=0b0, m=0b00, tr_ext=0b1, sel=0b00, session=0b00, target=0b0, q=0b0100) -> bool + + Set query parameter. + + :param int dr: dr. fixed to 0. + :param int m: m. fixed to 0. + :param int tr_ext: tr_ext. fixed to 1. + :param int sel: sel. the range is from 0 to 3. + :param int session: session. the range is from 0 to 3. + :param int target: target. the range is from 0 to 1. + :param int q: q. the range is from 0 to 8. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_query_param.png| + + +.. method:: UHFRFIDUnit.lock_mem_bank( kill_lock: int = 0b00, access_lock: int = 0b00, epc_lock: int = 0b00, tid_lock: int = 0b00, user_lock: int = 0b00, access: str = "00000000",) -> bool + + Lock memory bank. + + :param int kill_lock: kill lock. + + Options: + - UHFRFIDUnit.OPEN: open + - UHFRFIDUnit.LOCK: lock + - UHFRFIDUnit.PERMA_OPEN: perma open + - UHFRFIDUnit.PERMA_LOCK: perma lock + + :param int access_lock: access lock. + + Options: + - UHFRFIDUnit.OPEN: open + - UHFRFIDUnit.LOCK: lock + - UHFRFIDUnit.PERMA_OPEN: perma open + - UHFRFIDUnit.PERMA_LOCK: perma lock + + :param int epc_lock: epc lock. + + Options: + - UHFRFIDUnit.OPEN: open + - UHFRFIDUnit.LOCK: lock + - UHFRFIDUnit.PERMA_OPEN: perma open + - UHFRFIDUnit.PERMA_LOCK: perma lock + + :param int tid_lock: tid lock. + + Options: + - UHFRFIDUnit.OPEN: open + - UHFRFIDUnit.LOCK: lock + - UHFRFIDUnit.PERMA_OPEN: perma open + - UHFRFIDUnit.PERMA_LOCK: perma lock + + :param int user_lock: user lock. + + Options: + - UHFRFIDUnit.OPEN: open + - UHFRFIDUnit.LOCK: lock + - UHFRFIDUnit.PERMA_OPEN: perma open + - UHFRFIDUnit.PERMA_LOCK: perma lock + + :param str access: access password. hexadecimal string. + + UIFLOW2: + + |lock_mem_bank.png| + + +.. method:: UHFRFIDUnit.read_mem_bank(bank: int, offset: int, length: int, access_password: str = "00000000") -> str + + Read memory bank. + + :param int bank: memory bank. + + Options: + - UHFRFIDUnit.RFU: reserved + - UHFRFIDUnit.EPC: epc + - UHFRFIDUnit.TID: tid + - UHFRFIDUnit.USER: user + + :param int offset: offset. + :param int length: length. + :param str access_password: access password. hexadecimal string. + + :return str: data. hexadecimal string. + + UIFLOW2: + + |read_mem_bank.png| + + +.. method:: UHFRFIDUnit.write_mem_bank(bank: int, offset: int, data: str, access_password: str = "00000000") + + Write memory bank. + + :param int bank: memory bank. + + Options: + - UHFRFIDUnit.RFU: reserved + - UHFRFIDUnit.EPC: epc + - UHFRFIDUnit.TID: tid + - UHFRFIDUnit.USER: user + + :param int offset: offset. + :param str data: data. hexadecimal string. + :param str access_password: access password. hexadecimal string. + + UIFLOW2: + + |write_mem_bank.png| + + +Impinj Monza +^^^^^^^^^^^^^ + +.. method:: UHFRFIDUnit.get_impinj_monza_qt_sr(persistence, password: str = "00000000") -> bool + + Get Impinj Monza QT_SR. + + :param int persistence: persistence. 0x00 is volatile memory, 0x01 is non-volatile memory. + :param str password: access password. hexadecimal string. + + :return bool: QT_SR status. + + UIFLOW2: + + |get_impinj_monza_qt_sr.png| + + +.. method:: UHFRFIDUnit.set_impinj_monza_qt_sr(qt_sr: bool, persistence: int, password: str = "00000000") -> bool + + Set Impinj Monza QT_SR. + + :param bool qt_sr: QT_SR status. + :param int persistence: persistence. 0x00 is volatile memory, 0x01 is non-volatile memory. + :param str password: access password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_impinj_monza_qt_sr.png| + + +.. method:: UHFRFIDUnit.get_impinj_monza_qt_mem(persistence, password: str = "00000000") -> bool + + Set Impinj Monza QT_MEM. + + :param int persistence: persistence. 0x00 is volatile memory, 0x01 is non-volatile memory. + :param str password: access password. hexadecimal string. + + :return bool: QT_MEM status. + + UIFLOW2: + + |get_impinj_monza_qt_mem.png| + + +.. method:: UHFRFIDUnit.set_impinj_monza_qt_mem(qt_mem: bool, persistence: int, password: str = "00000000") -> bool + + Set Impinj Monza QT_MEM. + + :param bool qt_mem: QT_MEM status. + :param int persistence: persistence. 0x00 is volatile memory, 0x01 is non-volatile memory. + :param str password: access password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_impinj_monza_qt_mem.png| + + +NXP +^^^ + +.. method:: UHFRFIDUnit.nxp_eas_alarm() -> str + + Get NXP EAS alarm code. + + :return str: NXP EAS alarm code. hexadecimal string. + + UIFLOW2: + + |nxp_eas_alarm.png| + + +.. method:: UHFRFIDUnit.get_nxp_config_word(password: str = "00000000") -> int + + Get NXP config word. + + :param str password: access password. hexadecimal string. + + :return int: NXP config word. + + UIFLOW2: + + |get_nxp_config_word.png| + + +.. method:: UHFRFIDUnit.set_nxp_config_word(config_word: int, password: str = "00000000") -> bool + + Set NXP config word. + + :param int config_word: NXP config word. + :param str password: access password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |set_nxp_config_word.png| + + +.. method:: UHFRFIDUnit.nxp_read_protect(set: int, password: str = "00000000") -> bool + + Set NXP read protect. + + :param int set: set read protect. 0x00 is set read protect, 0x01 is reset read protect. + :param str password: access password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |nxp_read_protect.png| + + |nxp_read_protect1.png| + + +.. method:: UHFRFIDUnit.nxp_change_eas(set: int, password: str = "00000000") -> bool + + Change NXP EAS. + + :param int set: set EAS. 0x00 is set EAS, 0x01 is reset EAS. + :param str password: access password. hexadecimal string. + + :return bool: True if success, False if failed. + + UIFLOW2: + + |nxp_change_eas.png| + + |nxp_change_eas1.png| diff --git a/docs/en/units/uwb.rst b/docs/en/units/uwb.rst new file mode 100644 index 00000000..a59b3380 --- /dev/null +++ b/docs/en/units/uwb.rst @@ -0,0 +1,196 @@ + +UWBUnit +======= + +.. include:: ../refs/unit.uwb.ref + +UWB is a Unit which integrates the UWB(Ultra Wide Band) communication protocol which uses nanosecond pulses to locate objects and define position and orientation. The design uses the Ai-ThinkerBU01 Transceiver module which is based on Decawave's DW1000 design. The internal STM32 chip with its integrated ranging algorithm,is capable of 10cm positioning accuracy and also supports AT command control. Applications include: Indoor wireless tracking/range finding of assets,which works by triangulating the position of the base station/s and tag (the base station resolves the position information and outputs it to the tag). +The firmware currently carried by this Unit only supports the transmission of ranging information, and does not currently support the transmission of custom information. When in use, it supports the configuration of 4 base station devices (using different IDs), and only a single tag device is allowed to operate at the same time. + + +Support the following products: + +|UWBUnit| + +Micropython Anchor Example: + + .. literalinclude:: ../../../examples/unit/uwb/core2_uwb_anchor_example.py + :language: python + :linenos: + +Micropython Tag Example: + + .. literalinclude:: ../../../examples/unit/uwb/stickc_plus2_uwb_tag_example.py + :language: python + :linenos: + + +UIFLOW2 Anchor Example: + + |example_anchor.png| + +UIFLOW2 Tag Example: + + |example_tag.png| + +.. only:: builder_html + + |core2_uwb_anchor_example.m5f2| + + |stickc_plus2_uwb_tag_example.m5f2| + +class UWBUnit +------------- + +Constructors +------------ + +.. class:: UWBUnit(id, port, device_mode, device_id, verbose) + + Create a UWB unit object. + + :param id: UART ID. + :param port: The port that the unit is connected to. + :param device_mode: device mode. + :param device_id: device ID. + :param bool verbose: verbose output. + + UIFLOW2: + + |init.png| + + +Methods +------- + +.. method:: UWBUnit.get_distance(index) + + Get the distance to the anchor ID (0 ~ 3). + + :return (float): distance in meters. + :param int index: anchor ID (0 ~ 3). + + UIFLOW2: + + |get_distance.png| + +.. method:: UWBUnit.get_device_id() + + Get the device ID. + + :return (int): device ID. + + UIFLOW2: + + |get_device_id.png| + +.. method:: UWBUnit.get_device_mode() + + Get the device mode. + + :return (int): device mode. + + UIFLOW2: + + |get_device_mode.png| + +.. method:: UWBUnit.set_device_mode(mode, id) + + Set the device mode and ID. + + :param int mode: device mode. + Options: + - ``Anchor``: UWBUnit.ANCHOR + - ``Tag``: UWBUnit.TAG + :param int id: device ID. + + UIFLOW2: + + |set_device_mode.png| + +.. method:: UWBUnit.isconnected() + + Check if the UWB unit is connected. + + :return: True if connected, False otherwise. + + UIFLOW2: + + |isconnected.png| + +.. method:: UWBUnit.get_version() + + Get the UWB unit firmware version. + + :return: firmware version. + + UIFLOW2: + + |get_version.png| + +.. method:: UWBUnit.reset() + + Reset the UWB unit. + +.. method:: UWBUnit.set_measurement_interval(interval) + + Set the measurement interval. + + :param int interval: measurement interval. + + UIFLOW2: + + |set_measurement_interval.png| + +.. method:: UWBUnit.set_measurement(enable) + + Set the measurement output. + + :param bool enable: enable or disable measurement output. + + UIFLOW2: + + |set_measurement.png| + +.. method:: UWBUnit.set_callback(anchor, event, callback) + + Set the callback function for the anchor status. + + :param int anchor: anchor ID (0 ~ 3). + :param int event: anchor status. + Options: + - ``ONLINE``: UWBUnit.ONLINE + - ``OFFLINE``: UWBUnit.OFFLINE + :param callback: callback function. + + UIFLOW2: + + |set_callback.png| + +.. method:: UWBUnit.update() + + Update the distances and anchor status. + + UIFLOW2: + + |update.png| + + + +Constants +--------- + +.. data:: UWBUnit.UNKNOWN +.. data:: UWBUnit.ANCHOR +.. data:: UWBUnit.TAG + + device role + + +.. data:: UWBUnit.OFFLINE +.. data:: UWBUnit.ONLINE +.. data:: UWBUnit._mode_map + + device status + + diff --git a/examples/module/grbl/grbl_example.m5f2 b/examples/module/grbl/grbl_example.m5f2 new file mode 100644 index 00000000..3800fec8 --- /dev/null +++ b/examples/module/grbl/grbl_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"basic","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__basic_screen","createTime":1730340918058,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker"]},{"module":["module_grbl"]}],"units":[],"hats":[],"bases":[],"i2cs":[],"blockly":"truegrbl_00x70hello M5grbl_0hello M5grbl_0hello M5grbl_0hello M5grbl_0grbl_0MODE_ABSOLUTEgrbl_0grbl_055105grbl_0grbl_0true","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730340918058}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/module/grbl/grbl_example.py b/examples/module/grbl/grbl_example.py new file mode 100644 index 00000000..b29ec031 --- /dev/null +++ b/examples/module/grbl/grbl_example.py @@ -0,0 +1,44 @@ +import os, sys, io +import M5 +from M5 import * +from module import GRBLModule + + +grbl_0 = None + + +def setup(): + global grbl_0 + + M5.begin() + Widgets.fillScreen(0x222222) + + grbl_0 = GRBLModule(address=0x70) + print(grbl_0.get_message()) + print(grbl_0.get_status()) + print(grbl_0.get_idle_state()) + print(grbl_0.get_lock_state()) + grbl_0.set_mode(GRBLModule.MODE_ABSOLUTE) + grbl_0.unlock() + grbl_0.turn(5, 5, 10, 5) + grbl_0.wait_idle() + grbl_0.lock() + + +def loop(): + global grbl_0 + M5.update() + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/module/lorawan868/lorawan868_example_rx.m5f2 b/examples/module/lorawan868/lorawan868_example_rx.m5f2 new file mode 100644 index 00000000..658cb1cc --- /dev/null +++ b/examples/module/lorawan868/lorawan868_example_rx.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"basic","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__basic_screen","createTime":1730346281094,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker"]},{"module":["module_com_lorawan868"]}],"units":[],"hats":[],"bases":[],"i2cs":[],"blockly":"truelorawan868_011716lorawan868_0lorawan868_0005018000lorawan868_0Falsehello M5lorawan868_0hello M5lorawan868_0hello M5lorawan868_0lorawan868_0MODE_LORAlorawan868_00truelorawan868_0hello M5lorawan868_0lorawan868_00","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730346281094}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/module/lorawan868/lorawan868_example_rx.py b/examples/module/lorawan868/lorawan868_example_rx.py new file mode 100644 index 00000000..c6f62a7f --- /dev/null +++ b/examples/module/lorawan868/lorawan868_example_rx.py @@ -0,0 +1,46 @@ +import os, sys, io +import M5 +from M5 import * +from module import LoRaWAN868Module + + +lorawan868_0 = None + + +def setup(): + global lorawan868_0 + + M5.begin() + Widgets.fillScreen(0x222222) + + lorawan868_0 = LoRaWAN868Module(1, (17, 16)) + lorawan868_0.wake_up() + lorawan868_0.set_parameters(0, 0, 5, 0, 1, 8, 0, 0, 0) + lorawan868_0.set_auto_low_power(False) + print(lorawan868_0.query_chip_id()) + print(lorawan868_0.query_lorawan_mode()) + print(lorawan868_0.any()) + lorawan868_0.set_mode(LoRaWAN868Module.MODE_LORA) + lorawan868_0.enable_rx(0) + + +def loop(): + global lorawan868_0 + M5.update() + if lorawan868_0.any(): + print(lorawan868_0.receive_data()) + lorawan868_0.enable_rx(0) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/module/lorawan868/lorawan868_example_tx.m5f2 b/examples/module/lorawan868/lorawan868_example_tx.m5f2 new file mode 100644 index 00000000..16af9ee9 --- /dev/null +++ b/examples/module/lorawan868/lorawan868_example_tx.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"basic","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__basic_screen","createTime":1730346281094,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker"]},{"module":["module_com_lorawan868"]}],"units":[],"hats":[],"bases":[],"i2cs":[],"blockly":"truelorawan868_011617lorawan868_0lorawan868_0005018000lorawan868_0Falsehello M5lorawan868_0hello M5lorawan868_0hello M5lorawan868_0lorawan868_0MODE_LORAtruelorawan868_0Hello Lora!1","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730346281094}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/module/lorawan868/lorawan868_example_tx.py b/examples/module/lorawan868/lorawan868_example_tx.py new file mode 100644 index 00000000..cbd20407 --- /dev/null +++ b/examples/module/lorawan868/lorawan868_example_tx.py @@ -0,0 +1,45 @@ +import os, sys, io +import M5 +from M5 import * +from module import LoRaWAN868Module +import time + + +lorawan868_0 = None + + +def setup(): + global lorawan868_0 + + M5.begin() + Widgets.fillScreen(0x222222) + + lorawan868_0 = LoRaWAN868Module(1, (17, 16)) + lorawan868_0.wake_up() + lorawan868_0.set_parameters(0, 0, 5, 0, 1, 8, 0, 0, 0) + lorawan868_0.set_auto_low_power(False) + print(lorawan868_0.query_chip_id()) + print(lorawan868_0.query_lorawan_mode()) + print(lorawan868_0.any()) + lorawan868_0.set_mode(LoRaWAN868Module.MODE_LORA) + + +def loop(): + global lorawan868_0 + M5.update() + lorawan868_0.send_hex("Hello Lora!") + time.sleep(1) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/module/step_motor_driver/step_motor_driver.m5f2 b/examples/module/step_motor_driver/step_motor_driver.m5f2 new file mode 100644 index 00000000..32f1f025 --- /dev/null +++ b/examples/module/step_motor_driver/step_motor_driver.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"basic","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__basic_screen","createTime":1730340918058,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker"]},{"module":["module_stepmotor_driver"]}],"units":[],"hats":[],"bases":[],"i2cs":[],"blockly":"DIRtruestepmotor_driver_00x2716121517130hello M5stepmotor_driver_0hello M5stepmotor_driver_00hello M5stepmotor_driver_0MOTOR_Xhello M5stepmotor_driver_0stepmotor_driver_0MOTOR_XMOTOR_STATE_ENABLEstepmotor_driver_0MOTOR_STATE_ENABLEstepmotor_driver_0STEP_FULLstepmotor_driver_0MOTOR_X1stepmotor_driver_0MOTOR_X1000stepmotor_driver_0MOTOR_X1DIR0trueDIRstepmotor_driver_0MOTOR_X1stepmotor_driver_0MOTOR_X0DIRDIR2","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730340918058}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/module/step_motor_driver/step_motor_driver.py b/examples/module/step_motor_driver/step_motor_driver.py new file mode 100644 index 00000000..4ba63aca --- /dev/null +++ b/examples/module/step_motor_driver/step_motor_driver.py @@ -0,0 +1,60 @@ +import os, sys, io +import M5 +from M5 import * +from module import StepMotorDriverModule +import time + + +stepmotor_driver_0 = None + + +DIR = None + + +def setup(): + global stepmotor_driver_0, DIR + + M5.begin() + Widgets.fillScreen(0x222222) + + stepmotor_driver_0 = StepMotorDriverModule( + address=0x27, step_pin=(16, 12, 15), dir_pin=(17, 13, 0) + ) + print(stepmotor_driver_0.get_all_limit_switch_state()) + print(stepmotor_driver_0.get_limit_switch_state(0)) + print(stepmotor_driver_0.get_fault_io_state(StepMotorDriverModule.MOTOR_X)) + print(stepmotor_driver_0.get_firmware_version()) + stepmotor_driver_0.reset_motor( + StepMotorDriverModule.MOTOR_X, StepMotorDriverModule.MOTOR_STATE_ENABLE + ) + stepmotor_driver_0.set_motor_state(StepMotorDriverModule.MOTOR_STATE_ENABLE) + stepmotor_driver_0.set_microstep(StepMotorDriverModule.STEP_FULL) + stepmotor_driver_0.set_motor_direction(StepMotorDriverModule.MOTOR_X, 1) + stepmotor_driver_0.set_motor_pwm_freq(StepMotorDriverModule.MOTOR_X, 1000) + stepmotor_driver_0.motor_control(StepMotorDriverModule.MOTOR_X, 1) + DIR = 0 + + +def loop(): + global stepmotor_driver_0, DIR + M5.update() + if DIR: + stepmotor_driver_0.set_motor_direction(StepMotorDriverModule.MOTOR_X, 1) + else: + stepmotor_driver_0.set_motor_direction(StepMotorDriverModule.MOTOR_X, 0) + DIR = not DIR + time.sleep(2) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/roller485/roller485_485_fire_example.m5f2 b/examples/unit/roller485/roller485_485_fire_example.m5f2 new file mode 100644 index 00000000..69094f30 --- /dev/null +++ b/examples/unit/roller485/roller485_485_fire_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"fire","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__fire_screen","createTime":1730184002078,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true},{"name":"title0","type":"title","layer":1,"screenId":"builtin","screenName":"","id":"k4dXe$oxuMq5Vz_+","createTime":1730184005219,"x":0,"y":0,"color":"#ffffff","backgroundColor":"#0000FF","text":"Roller485 RS485 Example","textOffset":3,"font":"Widgets.FONTS.DejaVu18","isSelected":false},{"name":"label0","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"sIz_G3Ix&^ZUCW-h","createTime":1730184024893,"x":1,"y":63,"color":"#ffffff","backgroundColor":"#222222","text":"mode:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label1","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"s-gIiFLL%_1Gecg=","createTime":1730184027890,"x":2,"y":108,"color":"#ffffff","backgroundColor":"#222222","text":"motor state:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label2","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"x1v_=3!uz5wf2_Er","createTime":1730184038954,"x":2,"y":152,"color":"#ffffff","backgroundColor":"#222222","text":"speed:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label3","type":"label","layer":5,"screenId":"builtin","screenName":"","id":"fHxH%q=0LKE6Lu5v","createTime":1730184064193,"x":40,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"mode","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label4","type":"label","layer":6,"screenId":"builtin","screenName":"","id":"wF2UK5DOintH!Cyt","createTime":1730184067184,"x":126,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"on/off","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker","i2c"]},{"unit":["unit_rs485","unit_roller485"]}],"units":[{"type":"unit_rs485","name":"rs485_0","portList":["A","B","C","Custom"],"portType":"Custom","userPort":[13,15],"id":"lv8PP2G#W3uOgsC+","createTime":1730190872951,"initBlockId":"b#9/JFdQYN0wn?`=EMFa"},{"type":"unit_roller485","name":"roller485_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"wLhj!FS6FV=QkyJx","createTime":1730190872953,"bus":"Unit RS485","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"mode":"RS485","modeList":["I2C","RS485","RS485->I2C"],"initBlockId":"DnPG5Ws/{M^CZcb(QtQz"}],"hats":[],"bases":[],"i2cs":[{"id":"i2c1","portType":"A","userPort":[22,21],"freq":"100000","blockId":"DAGgY#jS6S%}ML_Z7J6("}],"blockly":"outputmodetruers485_02rs485_0False115200roller485_0RS4850rs485_0roller485_00output0moderoller485_0label0Labelmode:modelabel1Labelmotor state:outputBtnBWAS_CLICKEDoutputoutput0roller485_01outputBtnAWAS_CLICKEDmodeADD1mode1GTmode4mode1truelabel0Labelmode:modelabel1Labelmotor state:outputEQmode1roller485_020000roller485_0400label2Labelspeed:roller485_0EQmode2roller485_01000roller485_0400label2Labelposition:roller485_0EQmode3roller485_0400label2Labelcurrent:roller485_0EQmode4label2Labelencoder:roller485_0roller485_01mode","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730184002074}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/roller485/roller485_485_fire_example.py b/examples/unit/roller485/roller485_485_fire_example.py new file mode 100644 index 00000000..2cd2ded7 --- /dev/null +++ b/examples/unit/roller485/roller485_485_fire_example.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from hardware import * +from unit import Roller485Unit +from unit import RS485Unit + + +title0 = None +label0 = None +label1 = None +label2 = None +label3 = None +label4 = None +rs485_0 = None +roller485_0 = None + + +output = None +mode = None + + +def btn_b_was_clicked_event(state): + global title0, label0, label1, label2, label3, label4, rs485_0, roller485_0, output, mode + output = output ^ (0x01 << 0) + roller485_0.set_motor_output_state(output) + + +def btn_a_was_clicked_event(state): + global title0, label0, label1, label2, label3, label4, rs485_0, roller485_0, output, mode + mode = mode + 1 + if mode > 4: + mode = 1 + + +def setup(): + global title0, label0, label1, label2, label3, label4, rs485_0, roller485_0, output, mode + + M5.begin() + Widgets.fillScreen(0x222222) + title0 = Widgets.Title( + "Roller485 RS485 Example", 3, 0xFFFFFF, 0x0000FF, Widgets.FONTS.DejaVu18 + ) + label0 = Widgets.Label("mode:", 1, 63, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label1 = Widgets.Label("motor state:", 2, 108, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label2 = Widgets.Label("speed:", 2, 152, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label3 = Widgets.Label("mode", 40, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label4 = Widgets.Label("on/off", 126, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + + BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btn_b_was_clicked_event) + BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btn_a_was_clicked_event) + + rs485_0 = RS485Unit(2, port=(13, 15)) + rs485_0.init( + tx_pin=None, + rx_pin=None, + baudrate=115200, + data_bits=None, + stop_bits=None, + parity=None, + ctrl_pin=None, + ) + roller485_0 = Roller485Unit(rs485_0, address=0, mode=Roller485Unit.RS485_MODE) + roller485_0.set_motor_output_state(0) + output = 0 + mode = roller485_0.get_motor_mode() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + + +def loop(): + global title0, label0, label1, label2, label3, label4, rs485_0, roller485_0, output, mode + M5.update() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + if mode == 1: + roller485_0.set_motor_speed(20000) + roller485_0.set_speed_max_current(400) + label2.setText(str((str("speed:") + str((roller485_0.get_motor_speed_readback()))))) + elif mode == 2: + roller485_0.set_motor_position(1000) + roller485_0.set_position_max_current(400) + label2.setText(str((str("position:") + str((roller485_0.get_motor_position_readback()))))) + elif mode == 3: + roller485_0.set_motor_max_current(400) + label2.setText(str((str("current:") + str((roller485_0.get_motor_current_readback()))))) + elif mode == 4: + label2.setText(str((str("encoder:") + str((roller485_0.get_encoder_value()))))) + roller485_0.set_motor_mode(mode) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/roller485/roller485_485toi2c_fire_example.m5f2 b/examples/unit/roller485/roller485_485toi2c_fire_example.m5f2 new file mode 100644 index 00000000..cc23916d --- /dev/null +++ b/examples/unit/roller485/roller485_485toi2c_fire_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"fire","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__fire_screen","createTime":1730184002078,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true},{"name":"title0","type":"title","layer":1,"screenId":"builtin","screenName":"","id":"k4dXe$oxuMq5Vz_+","createTime":1730184005219,"x":0,"y":0,"color":"#ffffff","backgroundColor":"#0000FF","text":"Roller485 485ToI2C Example","textOffset":3,"font":"Widgets.FONTS.DejaVu18","isSelected":false},{"name":"label0","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"sIz_G3Ix&^ZUCW-h","createTime":1730184024893,"x":1,"y":63,"color":"#ffffff","backgroundColor":"#222222","text":"mode:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label1","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"s-gIiFLL%_1Gecg=","createTime":1730184027890,"x":2,"y":108,"color":"#ffffff","backgroundColor":"#222222","text":"motor state:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label2","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"x1v_=3!uz5wf2_Er","createTime":1730184038954,"x":2,"y":152,"color":"#ffffff","backgroundColor":"#222222","text":"speed:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label3","type":"label","layer":5,"screenId":"builtin","screenName":"","id":"fHxH%q=0LKE6Lu5v","createTime":1730184064193,"x":40,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"mode","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label4","type":"label","layer":6,"screenId":"builtin","screenName":"","id":"wF2UK5DOintH!Cyt","createTime":1730184067184,"x":126,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"on/off","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label5","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"z=M19Ya`qfw3@T1U","createTime":1730188897255,"x":182,"y":66,"color":"#ffffff","backgroundColor":"#222222","text":"temp:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label6","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"kUc+yPF9rRx9^*#C","createTime":1730188899922,"x":182,"y":131,"color":"#ffffff","backgroundColor":"#222222","text":"humi:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker","i2c"]},{"unit":["unit_env2","unit_rs485","unit_roller485"]}],"units":[{"type":"unit_env2","name":"env2_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"j!9a`M742mok2r`B","createTime":1730252585445,"bus":"iwVh@oqYkTc7hi!r","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"initBlockId":"L6.0%oEkm?-6kl3nA6j("},{"type":"unit_rs485","name":"rs485_0","portList":["A","B","C","Custom"],"portType":"Custom","userPort":[13,15],"id":"gZ3vA9z4N$eJy=eb","createTime":1730252585448,"initBlockId":"b#9/JFdQYN0wn?`=EMFa"},{"type":"unit_roller485","name":"roller485_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"iwVh@oqYkTc7hi!r","createTime":1730252585448,"bus":"Unit RS485","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"mode":"RS485->I2C","modeList":["I2C","RS485","RS485->I2C"],"initBlockId":"DnPG5Ws/{M^CZcb(QtQz"}],"hats":[],"bases":[],"i2cs":[{"id":"i2c1","portType":"A","userPort":[22,21],"freq":"100000"}],"blockly":"outputmodetruers485_02rs485_0False115200I2C\">roller485_0RS485->I2C0rs485_0env2_0roller485_00output0moderoller485_0label0Labelmode:modelabel1Labelmotor state:outputBtnBWAS_CLICKEDoutputoutput0roller485_01outputBtnAWAS_CLICKEDmodeADD1mode1GTmode4mode1truelabel0Labelmode:modelabel1Labelmotor state:outputlabel5co2:temp:env2_0label6humi:humi:env2_0EQmode1roller485_020000roller485_0400label2Labelspeed:roller485_0EQmode2roller485_01000roller485_0400label2Labelposition:roller485_0EQmode3roller485_0400label2Labelcurrent:roller485_0EQmode4label2Labelencoder:roller485_0roller485_01mode","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730184002074}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/roller485/roller485_485toi2c_fire_example.py b/examples/unit/roller485/roller485_485toi2c_fire_example.py new file mode 100644 index 00000000..516cdf1b --- /dev/null +++ b/examples/unit/roller485/roller485_485toi2c_fire_example.py @@ -0,0 +1,168 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from hardware import * +from unit import Roller485Unit +from unit import RS485Unit +from unit import ENVUnit + + +title0 = None +label5 = None +label0 = None +label6 = None +label1 = None +label2 = None +label3 = None +label4 = None +env2_0 = None +rs485_0 = None +roller485_0 = None + + +output = None +mode = None + + +def btn_b_was_clicked_event(state): + global \ + title0, \ + label5, \ + label0, \ + label6, \ + label1, \ + label2, \ + label3, \ + label4, \ + env2_0, \ + rs485_0, \ + roller485_0, \ + output, \ + mode + output = output ^ (0x01 << 0) + roller485_0.set_motor_output_state(output) + + +def btn_a_was_clicked_event(state): + global \ + title0, \ + label5, \ + label0, \ + label6, \ + label1, \ + label2, \ + label3, \ + label4, \ + env2_0, \ + rs485_0, \ + roller485_0, \ + output, \ + mode + mode = mode + 1 + if mode > 4: + mode = 1 + + +def setup(): + global \ + title0, \ + label5, \ + label0, \ + label6, \ + label1, \ + label2, \ + label3, \ + label4, \ + env2_0, \ + rs485_0, \ + roller485_0, \ + output, \ + mode + + M5.begin() + Widgets.fillScreen(0x222222) + title0 = Widgets.Title( + "Roller485 485ToI2C Example", 3, 0xFFFFFF, 0x0000FF, Widgets.FONTS.DejaVu18 + ) + label5 = Widgets.Label("temp:", 182, 66, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label0 = Widgets.Label("mode:", 1, 63, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label6 = Widgets.Label("humi:", 182, 131, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label1 = Widgets.Label("motor state:", 2, 108, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label2 = Widgets.Label("speed:", 2, 152, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label3 = Widgets.Label("mode", 40, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label4 = Widgets.Label("on/off", 126, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + + BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btn_b_was_clicked_event) + BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btn_a_was_clicked_event) + + rs485_0 = RS485Unit(2, port=(13, 15)) + rs485_0.init( + tx_pin=None, + rx_pin=None, + baudrate=115200, + data_bits=None, + stop_bits=None, + parity=None, + ctrl_pin=None, + ) + roller485_0 = Roller485Unit(rs485_0, address=0, mode=Roller485Unit.RS485_TO_I2C_MODE) + env2_0 = ENVUnit(i2c=roller485_0, type=2) + roller485_0.set_motor_output_state(0) + output = 0 + mode = roller485_0.get_motor_mode() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + + +def loop(): + global \ + title0, \ + label5, \ + label0, \ + label6, \ + label1, \ + label2, \ + label3, \ + label4, \ + env2_0, \ + rs485_0, \ + roller485_0, \ + output, \ + mode + M5.update() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + label5.setText(str((str("temp:") + str((env2_0.read_temperature()))))) + label6.setText(str((str("humi:") + str((env2_0.read_pressure()))))) + if mode == 1: + roller485_0.set_motor_speed(20000) + roller485_0.set_speed_max_current(400) + label2.setText(str((str("speed:") + str((roller485_0.get_motor_speed_readback()))))) + elif mode == 2: + roller485_0.set_motor_position(1000) + roller485_0.set_position_max_current(400) + label2.setText(str((str("position:") + str((roller485_0.get_motor_position_readback()))))) + elif mode == 3: + roller485_0.set_motor_max_current(400) + label2.setText(str((str("current:") + str((roller485_0.get_motor_current_readback()))))) + elif mode == 4: + label2.setText(str((str("encoder:") + str((roller485_0.get_encoder_value()))))) + roller485_0.set_motor_mode(mode) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/roller485/roller485_i2c_fire_example.m5f2 b/examples/unit/roller485/roller485_i2c_fire_example.m5f2 new file mode 100644 index 00000000..8c9334fc --- /dev/null +++ b/examples/unit/roller485/roller485_i2c_fire_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"fire","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__fire_screen","createTime":1730184002078,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true},{"name":"title0","type":"title","layer":1,"screenId":"builtin","screenName":"","id":"k4dXe$oxuMq5Vz_+","createTime":1730184005219,"x":0,"y":0,"color":"#ffffff","backgroundColor":"#0000FF","text":"Roller485 I2C Example","textOffset":3,"font":"Widgets.FONTS.DejaVu18","isSelected":false},{"name":"label0","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"sIz_G3Ix&^ZUCW-h","createTime":1730184024893,"x":1,"y":63,"color":"#ffffff","backgroundColor":"#222222","text":"mode:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label1","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"s-gIiFLL%_1Gecg=","createTime":1730184027890,"x":2,"y":108,"color":"#ffffff","backgroundColor":"#222222","text":"motor state:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label2","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"x1v_=3!uz5wf2_Er","createTime":1730184038954,"x":2,"y":152,"color":"#ffffff","backgroundColor":"#222222","text":"speed:","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label3","type":"label","layer":5,"screenId":"builtin","screenName":"","id":"fHxH%q=0LKE6Lu5v","createTime":1730184064193,"x":40,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"mode","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label4","type":"label","layer":6,"screenId":"builtin","screenName":"","id":"wF2UK5DOintH!Cyt","createTime":1730184067184,"x":126,"y":215,"color":"#ffffff","backgroundColor":"#222222","text":"on/off","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","rgb","speaker","i2c"]},{"unit":["unit_roller485"]}],"units":[{"type":"unit_roller485","name":"roller485_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"wS-+$c898^+wnH3G","createTime":1730190905087,"bus":"i2c1","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"mode":"I2C","modeList":["I2C","RS485","RS485->I2C"],"initBlockId":"DnPG5Ws/{M^CZcb(QtQz"}],"hats":[],"bases":[],"i2cs":[{"id":"i2c1","portType":"A","userPort":[22,21],"freq":"100000","blockId":"M*9Vo`W!Yl:f8^Uw)|lJ"}],"blockly":"outputmodetrue11000002221roller485_0I2C0x64roller485_00outputroller485_0moderoller485_0label0Labelmode:modelabel1Labelmotor state:outputBtnBWAS_CLICKEDoutputoutput0roller485_01outputBtnAWAS_CLICKEDmodeADD1mode1GTmode4mode10truelabel0Labelmode:modelabel1Labelmotor state:outputEQmode1roller485_020000roller485_0400label2Labelspeed:roller485_0EQmode2roller485_01000roller485_0400label2Labelposition:roller485_0EQmode3roller485_0400label2Labelcurrent:roller485_0EQmode4label2Labelencoder:roller485_0roller485_01mode","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730184002074}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/roller485/roller485_i2c_fire_example.py b/examples/unit/roller485/roller485_i2c_fire_example.py new file mode 100644 index 00000000..5ab6a3e0 --- /dev/null +++ b/examples/unit/roller485/roller485_i2c_fire_example.py @@ -0,0 +1,95 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from hardware import * +from unit import Roller485Unit + + +title0 = None +label0 = None +label1 = None +label2 = None +label3 = None +label4 = None +i2c1 = None +roller485_0 = None + + +output = None +mode = None + + +def btn_b_was_clicked_event(state): + global title0, label0, label1, label2, label3, label4, i2c1, roller485_0, output, mode + output = output ^ (0x01 << 0) + roller485_0.set_motor_output_state(output) + + +def btn_a_was_clicked_event(state): + global title0, label0, label1, label2, label3, label4, i2c1, roller485_0, output, mode + mode = mode + 1 + if mode > 4: + mode = 1 + + +def setup(): + global title0, label0, label1, label2, label3, label4, i2c1, roller485_0, output, mode + + M5.begin() + Widgets.fillScreen(0x222222) + title0 = Widgets.Title("Roller485 I2C Example", 3, 0xFFFFFF, 0x0000FF, Widgets.FONTS.DejaVu18) + label0 = Widgets.Label("mode:", 1, 63, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label1 = Widgets.Label("motor state:", 2, 108, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label2 = Widgets.Label("speed:", 2, 152, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label3 = Widgets.Label("mode", 40, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label4 = Widgets.Label("on/off", 126, 215, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + + BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btn_b_was_clicked_event) + BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btn_a_was_clicked_event) + + i2c1 = I2C(1, scl=Pin(22), sda=Pin(21), freq=100000) + roller485_0 = Roller485Unit(i2c1, address=0x64, mode=Roller485Unit.I2C_MODE) + roller485_0.set_motor_output_state(0) + output = roller485_0.get_motor_output_state() + mode = roller485_0.get_motor_mode() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + + +def loop(): + global title0, label0, label1, label2, label3, label4, i2c1, roller485_0, output, mode + M5.update() + label0.setText(str((str("mode:") + str(mode)))) + label1.setText(str((str("motor state:") + str(output)))) + if mode == 1: + roller485_0.set_motor_speed(20000) + roller485_0.set_speed_max_current(400) + label2.setText(str((str("speed:") + str((roller485_0.get_motor_speed_readback()))))) + elif mode == 2: + roller485_0.set_motor_position(1000) + roller485_0.set_position_max_current(400) + label2.setText(str((str("position:") + str((roller485_0.get_motor_position_readback()))))) + elif mode == 3: + roller485_0.set_motor_max_current(400) + label2.setText(str((str("current:") + str((roller485_0.get_motor_current_readback()))))) + elif mode == 4: + label2.setText(str((str("encoder:") + str((roller485_0.get_encoder_value()))))) + roller485_0.set_motor_mode(mode) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/timerpwr/atoms3_timerpwr_example.m5f2 b/examples/unit/timerpwr/atoms3_timerpwr_example.m5f2 new file mode 100644 index 00000000..790df7e6 --- /dev/null +++ b/examples/unit/timerpwr/atoms3_timerpwr_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"atoms3","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__atoms3_screen","createTime":1730087726993,"x":0,"y":0,"width":128,"height":128,"backgroundColor":"#000000","size":0,"isSelected":true},{"name":"label0","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"h#Gy94CuLs*-k9E+","createTime":1730089568679,"x":13,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"OUT","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label1","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"dHo%Di13OpyK80+w","createTime":1730089568679,"x":9,"y":24,"color":"#ffffff","backgroundColor":"#222222","text":"label1","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label2","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"tIYfMIwG8*jCfRAR","createTime":1730089568679,"x":9,"y":42,"color":"#ffffff","backgroundColor":"#222222","text":"label2","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label3","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"g$l+KTb-xlyvCbLA","createTime":1730089568679,"x":54,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"BAT","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label4","type":"label","layer":5,"screenId":"builtin","screenName":"","id":"cQ&%50`iPxnFl3T7","createTime":1730089568679,"x":49,"y":24,"color":"#ffffff","backgroundColor":"#222222","text":"label4","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label5","type":"label","layer":6,"screenId":"builtin","screenName":"","id":"o=#awo`Dm0gAwtT3","createTime":1730089568679,"x":49,"y":42,"color":"#ffffff","backgroundColor":"#222222","text":"label5","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label6","type":"label","layer":7,"screenId":"builtin","screenName":"","id":"p`7VO&nDp`2J7Zp$","createTime":1730089568679,"x":93,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"USB","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label7","type":"label","layer":8,"screenId":"builtin","screenName":"","id":"b7vwCAhGPmsweMK@","createTime":1730089568679,"x":88,"y":24,"color":"#ffffff","backgroundColor":"#222222","text":"label7","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"label8","type":"label","layer":9,"screenId":"builtin","screenName":"","id":"d#TYA9$De^ZdS^Np","createTime":1730089568679,"x":88,"y":42,"color":"#ffffff","backgroundColor":"#222222","text":"label8","engine":"gfx","font":"Widgets.FONTS.DejaVu9","rotation":0,"isSelected":false},{"name":"rect0","type":"rect","layer":10,"screenId":"builtin","screenName":"","id":"cbyi%A0#g$^ST0gD","createTime":1730089665721,"x":6,"y":98,"width":24,"height":24,"borderColor":"#00ff00","backgroundColor":"#00ff00","isSelected":false},{"name":"rect1","type":"rect","layer":12,"screenId":"builtin","screenName":"","id":"vdD7GXY&OQhWaV$Q","createTime":1730089665721,"x":37,"y":98,"width":24,"height":24,"borderColor":"#00ff00","backgroundColor":"#00ff00","isSelected":false},{"name":"label10","type":"label","layer":13,"screenId":"builtin","screenName":"","id":"ffflhFMIFwyxmHNX","createTime":1730089679119,"x":13,"y":103,"color":"#ffffff","backgroundColor":"#00ff00","text":"U","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"rect2","type":"rect","layer":14,"screenId":"builtin","screenName":"","id":"vYdNJiLHEQ6YpJj^","createTime":1730089665721,"x":67,"y":98,"width":24,"height":24,"borderColor":"#00ff00","backgroundColor":"#00ff00","isSelected":false},{"name":"rect3","type":"rect","layer":15,"screenId":"builtin","screenName":"","id":"tfV7pluN8vkSymE0","createTime":1730089665721,"x":97,"y":98,"width":24,"height":24,"borderColor":"#00ff00","backgroundColor":"#00ff00","isSelected":false},{"name":"label11","type":"label","layer":16,"screenId":"builtin","screenName":"","id":"sxBrn#4L4VphMhG7","createTime":1730089679119,"x":45,"y":103,"color":"#ffffff","backgroundColor":"#00ff00","text":"A","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"label12","type":"label","layer":17,"screenId":"builtin","screenName":"","id":"kbi_Lqms$yGnUyZH","createTime":1730089679119,"x":75,"y":103,"color":"#ffffff","backgroundColor":"#00ff00","text":"B","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"label9","type":"label","layer":17,"screenId":"builtin","screenName":"","id":"rI3qO0`UjlR4nr9X","createTime":1730089679119,"x":105,"y":103,"color":"#ffffff","backgroundColor":"#00ff00","text":"C","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","ir","i2c"]},{"unit":["unit_timerpwr"]}],"units":[{"type":"unit_timerpwr","name":"timerpwr_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"srQ6#A+*oxH2EZ&s","createTime":1730098334048,"bus":"i2c0","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"initBlockId":"]Uu;~O;V%AH/YajzZ9_M"}],"hats":[],"bases":[],"i2cs":[{"id":"i2c0","portType":"A","userPort":[22,21],"freq":"100000","blockId":"+FxQ:iK:/-OK{nua0iZX"}],"blockly":"entrue010000012timerpwr_00x56enTRUEtimerpwr_0TRIG_ALLtimerpwr_0TRIG_ALLtruelabel1Labeltimerpwr_0label2Labeltimerpwr_0label4Labeltimerpwr_0label5Labeltimerpwr_0label7Labeltimerpwr_0label8Labeltimerpwr_0timerpwr_0BtnAWAS_CLICKEDtimerpwr_000300010timerpwr_0CHARGINGrect3rgb02550rgb02550label9Clabel9rgb02550rgb02550timerpwr_0NOT_CHARGINGrect3rgb25500rgb25500label9Clabel9rgb25500rgb25500timerpwr_0ARELEASEDhello M5timerpwr_00rect1rgb02550rgb02550label11Alabel11rgb02550rgb02550timerpwr_0APRESSEDhello M5timerpwr_00rect1rgb25500rgb25500label11Alabel11rgb25500rgb25500timerpwr_0BRELEASEDhello M5timerpwr_01rect2rgb02550rgb02550label12Blabel12rgb02550rgb02550timerpwr_0BPRESSEDhello M5timerpwr_01rect2rgb25500rgb25500label12Blabel12rgb25500rgb25500timerpwr_0USB_INSERTEDrect0rgb02550rgb02550label10Ulabel10rgb02550rgb02550timerpwr_0USB_REMOVEDrect0rgb25500rgb25500label10Ulabel10rgb25500rgb25500","screen":[{"simulationName":"Built-in","type":"builtin","width":128,"height":128,"scale":1.3,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730087726990}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/timerpwr/atoms3_timerpwr_example.py b/examples/unit/timerpwr/atoms3_timerpwr_example.py new file mode 100644 index 00000000..d838bbb9 --- /dev/null +++ b/examples/unit/timerpwr/atoms3_timerpwr_example.py @@ -0,0 +1,383 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from unit import TimerPWRUnit +from hardware import * + + +label0 = None +label1 = None +label2 = None +label3 = None +label4 = None +label5 = None +label6 = None +label7 = None +label8 = None +rect0 = None +rect1 = None +label10 = None +rect2 = None +rect3 = None +label11 = None +label12 = None +label9 = None +i2c0 = None +timerpwr_0 = None + + +en = None + + +def timerpwr_0_btna_released_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + print(timerpwr_0.get_button_status(0)) + rect1.setColor(color=0x00FF00, fill_c=0x00FF00) + label11.setText(str("A")) + label11.setColor(0x00FF00, 0x00FF00) + + +def timerpwr_0_btna_pressed_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + print(timerpwr_0.get_button_status(0)) + rect1.setColor(color=0xFF0000, fill_c=0xFF0000) + label11.setText(str("A")) + label11.setColor(0xFF0000, 0xFF0000) + + +def timerpwr_0_btnb_released_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + print(timerpwr_0.get_button_status(1)) + rect2.setColor(color=0x00FF00, fill_c=0x00FF00) + label12.setText(str("B")) + label12.setColor(0x00FF00, 0x00FF00) + + +def timerpwr_0_btnb_pressed_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + print(timerpwr_0.get_button_status(1)) + rect2.setColor(color=0xFF0000, fill_c=0xFF0000) + label12.setText(str("B")) + label12.setColor(0xFF0000, 0xFF0000) + + +def timerpwr_0_usb_inserted_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + rect0.setColor(color=0x00FF00, fill_c=0x00FF00) + label10.setText(str("U")) + label10.setColor(0x00FF00, 0x00FF00) + + +def timerpwr_0_usb_removed_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + rect0.setColor(color=0xFF0000, fill_c=0xFF0000) + label10.setText(str("U")) + label10.setColor(0xFF0000, 0xFF0000) + + +def timerpwr_0_not_charging_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + rect3.setColor(color=0xFF0000, fill_c=0xFF0000) + label9.setText(str("C")) + label9.setColor(0xFF0000, 0xFF0000) + + +def timerpwr_0_charging_event(args): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + rect3.setColor(color=0x00FF00, fill_c=0x00FF00) + label9.setText(str("C")) + label9.setColor(0x00FF00, 0x00FF00) + + +def btnA_wasClicked_event(state): # noqa: N802 + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + timerpwr_0.sleep_cycle(0, 0, 30, 0, 0, 10) + + +def setup(): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + + M5.begin() + label0 = Widgets.Label("OUT", 13, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label1 = Widgets.Label("label1", 9, 24, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label2 = Widgets.Label("label2", 9, 42, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label3 = Widgets.Label("BAT", 54, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label4 = Widgets.Label("label4", 49, 24, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label5 = Widgets.Label("label5", 49, 42, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label6 = Widgets.Label("USB", 93, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label7 = Widgets.Label("label7", 88, 24, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + label8 = Widgets.Label("label8", 88, 42, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu9) + rect0 = Widgets.Rectangle(6, 98, 24, 24, 0x00FF00, 0x00FF00) + rect1 = Widgets.Rectangle(37, 98, 24, 24, 0x00FF00, 0x00FF00) + label10 = Widgets.Label("U", 13, 103, 1.0, 0xFFFFFF, 0x00FF00, Widgets.FONTS.DejaVu12) + rect2 = Widgets.Rectangle(67, 98, 24, 24, 0x00FF00, 0x00FF00) + rect3 = Widgets.Rectangle(97, 98, 24, 24, 0x00FF00, 0x00FF00) + label11 = Widgets.Label("A", 45, 103, 1.0, 0xFFFFFF, 0x00FF00, Widgets.FONTS.DejaVu12) + label12 = Widgets.Label("B", 75, 103, 1.0, 0xFFFFFF, 0x00FF00, Widgets.FONTS.DejaVu12) + label9 = Widgets.Label("C", 105, 103, 1.0, 0xFFFFFF, 0x00FF00, Widgets.FONTS.DejaVu12) + + BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btnA_wasClicked_event) + + i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000) + timerpwr_0 = TimerPWRUnit(i2c0, 0x56) + timerpwr_0.set_callback(timerpwr_0.EVENT_BUTTONA_RELEASED, timerpwr_0_btna_released_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_BUTTONA_PRESSED, timerpwr_0_btna_pressed_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_BUTTONB_RELEASED, timerpwr_0_btnb_released_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_BUTTONB_PRESSED, timerpwr_0_btnb_pressed_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_USB_INSERTED, timerpwr_0_usb_inserted_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_USB_REMOVED, timerpwr_0_usb_removed_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_NOT_CHARGING, timerpwr_0_not_charging_event) + timerpwr_0.set_callback(timerpwr_0.EVENT_CHARGING, timerpwr_0_charging_event) + en = True + timerpwr_0.set_wakeup_trigger(timerpwr_0.TRIG_ALL) + timerpwr_0.set_sleep_trigger(timerpwr_0.TRIG_ALL) + + +def loop(): + global \ + label0, \ + label1, \ + label2, \ + label3, \ + label4, \ + label5, \ + label6, \ + label7, \ + label8, \ + rect0, \ + rect1, \ + label10, \ + rect2, \ + rect3, \ + label11, \ + label12, \ + label9, \ + i2c0, \ + timerpwr_0, \ + en + M5.update() + label1.setText(str(timerpwr_0.get_grove_voltage())) + label2.setText(str(timerpwr_0.get_battery_current())) + label4.setText(str(timerpwr_0.get_battery_voltage())) + label5.setText(str(timerpwr_0.get_battery_current())) + label7.setText(str(timerpwr_0.get_usb_voltage())) + label8.setText(str(timerpwr_0.get_usb_current())) + timerpwr_0.tick() + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/uhf_rfid/cores3_uhf_rfid_example.m5f2 b/examples/unit/uhf_rfid/cores3_uhf_rfid_example.m5f2 new file mode 100644 index 00000000..3ded9448 --- /dev/null +++ b/examples/unit/uhf_rfid/cores3_uhf_rfid_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"cores3","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__cores3_screen","createTime":1727597719031,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","touch","als","mic"]},{"unit":["unit_nbiot2","unit_uhfrfid"]}],"units":[{"type":"unit_nbiot2","name":"nbiot2_0","portList":["A","B","C","Custom"],"portType":"C","userPort":[22,21],"id":"dM9!662lyCK&gBo_","createTime":1730184495111},{"type":"unit_uhfrfid","name":"uhfrfid_0","portList":["A","B","C","Custom"],"portType":"C","userPort":[22,21],"id":"iJ1X^xPyN^yVXGXY","createTime":1730184495112,"initBlockId":"hg(C#;eS8D#v,K|$$.V["}],"hats":[],"bases":[],"i2cs":[],"blockly":"epctrueuhfrfid_02epcuhfrfid_0hello M5epcuhfrfid_0S00b000RFU0x20Falseepcuhfrfid_0RFU0x001234567800000000hello M5uhfrfid_0RFU0x00400000000true","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1727597719015}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/uhf_rfid/cores3_uhf_rfid_example.py b/examples/unit/uhf_rfid/cores3_uhf_rfid_example.py new file mode 100644 index 00000000..5c3feeb7 --- /dev/null +++ b/examples/unit/uhf_rfid/cores3_uhf_rfid_example.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from unit import UHFRFIDUnit + + +nbiot2_0 = None +uhfrfid_0 = None + + +epc = None + + +def setup(): + global nbiot2_0, uhfrfid_0, epc + + M5.begin() + Widgets.fillScreen(0x222222) + + uhfrfid_0 = UHFRFIDUnit(2, port=(18, 17)) + epc = uhfrfid_0.inventory() + print(epc) + uhfrfid_0.select(UHFRFIDUnit.S0, 0b000, UHFRFIDUnit.RFU, 0x20, False, epc) + uhfrfid_0.write_mem_bank(UHFRFIDUnit.RFU, 0x00, "12345678", "00000000") + print(uhfrfid_0.read_mem_bank(UHFRFIDUnit.RFU, 0x00, 4, "00000000")) + + +def loop(): + global nbiot2_0, uhfrfid_0, epc + M5.update() + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/uwb/core2_uwb_anchor_example.m5f2 b/examples/unit/uwb/core2_uwb_anchor_example.m5f2 new file mode 100644 index 00000000..3e075a6f --- /dev/null +++ b/examples/unit/uwb/core2_uwb_anchor_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"stickc-plus2","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__stickc-plus2_screen","createTime":1730193842205,"x":0,"y":0,"width":135,"height":240,"backgroundColor":"#000000","size":0,"isSelected":true},{"name":"label0","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"kS!fatSHiT!az1aV","createTime":1730347329701,"x":29,"y":36,"color":"#ffffff","backgroundColor":"#222222","text":"label0","engine":"gfx","font":"Widgets.FONTS.DejaVu24","rotation":0,"isSelected":false,"width":76,"height":28},{"name":"label1","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"e1DM86gJW1CyY2L+","createTime":1730347366455,"x":44,"y":98,"color":"#ffffff","backgroundColor":"#222222","text":"0","engine":"gfx","font":"Widgets.FONTS.DejaVu72","rotation":0,"isSelected":false,"width":46,"height":82}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","ir"]},{"unit":["unit_uwb"]}],"units":[{"type":"unit_uwb","name":"uwb_0","portList":["A","B","C","Custom"],"portType":"A","userPort":[22,21],"id":"wq&#vJ&koZE@^zfm","createTime":1730344044442}],"hats":[],"bases":[],"i2cs":[],"blockly":"anchor_idtrueanchor_id0uwb_0anchorFalse20hello M5uwb_0hello M5uwb_0EQuwb_01label0Anchorlabel0Taglabel1Labeluwb_0trueBtnAWAS_CLICKEDanchor_idREMAINDER1ADD1anchor_id14uwb_0anchor2anchor_idlabel1Labeluwb_0","screen":[{"simulationName":"Built-in","type":"builtin","width":135,"height":240,"scale":0.92,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730193842203}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/uwb/core2_uwb_anchor_example.py b/examples/unit/uwb/core2_uwb_anchor_example.py new file mode 100644 index 00000000..66145f42 --- /dev/null +++ b/examples/unit/uwb/core2_uwb_anchor_example.py @@ -0,0 +1,248 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from unit import UWBUnit + + +label0 = None +label4 = None +label1 = None +label5 = None +label2 = None +label6 = None +label3 = None +label7 = None +circle0 = None +circle1 = None +circle2 = None +circle3 = None +uwb_0 = None + + +def uwb_0_0_online_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle0.setColor(color=0x33FF33, fill_c=0x33FF33) + + +def uwb_0_1_online_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle1.setColor(color=0x33FF33, fill_c=0x33FF33) + + +def uwb_0_2_online_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle2.setColor(color=0x33FF33, fill_c=0x33FF33) + + +def uwb_0_3_online_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle3.setColor(color=0x33FF33, fill_c=0x33FF33) + + +def uwb_0_0_offline_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle0.setColor(color=0xFF0000, fill_c=0xFF0000) + + +def uwb_0_1_offline_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle1.setColor(color=0xFF0000, fill_c=0xFF0000) + + +def uwb_0_2_offline_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle2.setColor(color=0xFF0000, fill_c=0xFF0000) + + +def uwb_0_3_offline_event(args): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + circle3.setColor(color=0xFF0000, fill_c=0xFF0000) + + +def setup(): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + + M5.begin() + Widgets.fillScreen(0x222222) + label0 = Widgets.Label("Anchor0", 0, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label4 = Widgets.Label("label4", 0, 120, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu12) + label1 = Widgets.Label("Anchor1", 80, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label5 = Widgets.Label("label5", 80, 120, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu12) + label2 = Widgets.Label("Anchor2", 160, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label6 = Widgets.Label("label6", 160, 120, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu12) + label3 = Widgets.Label("Anchor3", 240, 4, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18) + label7 = Widgets.Label("label7", 240, 120, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu12) + circle0 = Widgets.Circle(38, 50, 15, 0xFF0000, 0xFF0000) + circle1 = Widgets.Circle(118, 52, 15, 0xFF0000, 0xFF0000) + circle2 = Widgets.Circle(193, 51, 15, 0xFF0000, 0xFF0000) + circle3 = Widgets.Circle(273, 51, 15, 0xFF0000, 0xFF0000) + + uwb_0 = UWBUnit(2, port=(33, 32), device_mode=UWBUnit.TAG, verbose=False) + uwb_0.set_callback(0, uwb_0.ONLINE, uwb_0_0_online_event) + uwb_0.set_callback(1, uwb_0.ONLINE, uwb_0_1_online_event) + uwb_0.set_callback(2, uwb_0.ONLINE, uwb_0_2_online_event) + uwb_0.set_callback(3, uwb_0.ONLINE, uwb_0_3_online_event) + uwb_0.set_callback(0, uwb_0.OFFLINE, uwb_0_0_offline_event) + uwb_0.set_callback(1, uwb_0.OFFLINE, uwb_0_1_offline_event) + uwb_0.set_callback(2, uwb_0.OFFLINE, uwb_0_2_offline_event) + uwb_0.set_callback(3, uwb_0.OFFLINE, uwb_0_3_offline_event) + uwb_0.set_measurement_interval(5) + uwb_0.set_measurement(True) + + +def loop(): + global \ + label0, \ + label4, \ + label1, \ + label5, \ + label2, \ + label6, \ + label3, \ + label7, \ + circle0, \ + circle1, \ + circle2, \ + circle3, \ + uwb_0 + M5.update() + uwb_0.update() + label4.setText(str(uwb_0.get_distance(0))) + label5.setText(str(uwb_0.get_distance(1))) + label6.setText(str(uwb_0.get_distance(2))) + label7.setText(str(uwb_0.get_distance(3))) + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/examples/unit/uwb/stickc_plus2_uwb_tag_example.m5f2 b/examples/unit/uwb/stickc_plus2_uwb_tag_example.m5f2 new file mode 100644 index 00000000..c3968e7a --- /dev/null +++ b/examples/unit/uwb/stickc_plus2_uwb_tag_example.m5f2 @@ -0,0 +1 @@ +{"version":"V2.0","versionNumber":"V2.1.6","type":"core2","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__core2_screen","createTime":1730348024274,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true},{"name":"label0","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"u%9=TBGOFi3+dAo*","createTime":1730348147420,"x":0,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"Anchor0","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label1","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"d^+4pZnABlouZj4-","createTime":1730348147420,"x":80,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"Anchor1","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label2","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"wj@iYvqvJu*qBOE+","createTime":1730348147420,"x":160,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"Anchor2","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"label3","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"tbkJ00=&tvIOqkTQ","createTime":1730348147420,"x":240,"y":4,"color":"#ffffff","backgroundColor":"#222222","text":"Anchor3","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false},{"name":"circle0","type":"circle","layer":5,"screenId":"builtin","screenName":"","id":"cd!57B`5Xhuy!!tO","createTime":1730348285356,"x":38,"y":50,"radius":15,"borderColor":"#ff0000","backgroundColor":"#ff0000","isSelected":false},{"name":"circle1","type":"circle","layer":6,"screenId":"builtin","screenName":"","id":"dp#PQO*zmG#e533=","createTime":1730348285356,"x":118,"y":52,"radius":15,"borderColor":"#ff0000","backgroundColor":"#ff0000","isSelected":false},{"name":"circle2","type":"circle","layer":7,"screenId":"builtin","screenName":"","id":"nCeqlsaLpo5oArX6","createTime":1730348285356,"x":193,"y":51,"radius":15,"borderColor":"#ff0000","backgroundColor":"#ff0000","isSelected":false},{"name":"circle3","type":"circle","layer":8,"screenId":"builtin","screenName":"","id":"bCzT-s%&^hu%P_r#","createTime":1730348285356,"x":273,"y":51,"radius":15,"borderColor":"#ff0000","backgroundColor":"#ff0000","isSelected":false},{"name":"label4","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"wHsiIw$&V!B4AHy^","createTime":1730355308519,"x":0,"y":120,"color":"#ffffff","backgroundColor":"#222222","text":"label4","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"label5","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"oDFN6ECk0%LAxQOt","createTime":1730355309833,"x":80,"y":120,"color":"#ffffff","backgroundColor":"#222222","text":"label5","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"label6","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"j0Y246dbGqd&ok%8","createTime":1730355310995,"x":160,"y":120,"color":"#ffffff","backgroundColor":"#222222","text":"label6","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false},{"name":"label7","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"w*z@6vNLl%3u0a1j","createTime":1730355312177,"x":240,"y":120,"color":"#ffffff","backgroundColor":"#222222","text":"label7","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","touch","mic"]},{"unit":["unit_uwb"]}],"units":[{"type":"unit_uwb","name":"uwb_0","portList":["A","B","C","Custom"],"portType":"A","userPort":[22,21],"id":"bVT!biH#`GocD!@C","createTime":1730356247580,"initBlockId":"}Pyw{F/=2UTj,L5QMi]]"}],"hats":[],"bases":[],"i2cs":[],"blockly":"trueuwb_0tagFalse20uwb_05uwb_0Trueuwb_0ONLINE0circle0palette#33ff33palette#33ff33trueuwb_0label4Labeluwb_00label5Labeluwb_01label6Labeluwb_02label7Labeluwb_03uwb_0ONLINE1circle1palette#33ff33palette#33ff33uwb_0ONLINE2circle2palette#33ff33palette#33ff33uwb_0ONLINE3circle3palette#33ff33palette#33ff33uwb_0OFFLINE0circle0palette#ff0000palette#ff0000uwb_0OFFLINE1circle1palette#ff0000palette#ff0000uwb_0OFFLINE2circle2palette#ff0000palette#ff0000uwb_0OFFLINE3circle3palette#ff0000palette#ff0000","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1730348024271}],"logicWhenNum":0,"customList":[]} \ No newline at end of file diff --git a/examples/unit/uwb/stickc_plus2_uwb_tag_example.py b/examples/unit/uwb/stickc_plus2_uwb_tag_example.py new file mode 100644 index 00000000..60e4cb83 --- /dev/null +++ b/examples/unit/uwb/stickc_plus2_uwb_tag_example.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import os, sys, io +import M5 +from M5 import * +from hardware import * +from unit import UWBUnit + + +label0 = None +label1 = None +uwb_0 = None + + +anchor_id = None + + +def btnA_wasClicked_event(state): # noqa: N802 + global label0, label1, uwb_0, anchor_id + anchor_id = (anchor_id + 1) % 4 + uwb_0.set_device_mode(UWBUnit.ANCHOR, anchor_id) + label1.setText(str(uwb_0.get_device_id())) + + +def setup(): + global label0, label1, uwb_0, anchor_id + + M5.begin() + label0 = Widgets.Label("label0", 29, 36, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu24) + label1 = Widgets.Label("0", 44, 98, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu72) + + BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btnA_wasClicked_event) + + anchor_id = 0 + uwb_0 = UWBUnit(2, port=(33, 32), device_mode=UWBUnit.ANCHOR, device_id=0, verbose=False) + print(uwb_0.isconnected()) + print(uwb_0.get_version()) + if (uwb_0.get_device_mode()) == 1: + label0.setText(str("Anchor")) + else: + label0.setText(str("Tag")) + label1.setText(str(uwb_0.get_device_id())) + + +def loop(): + global label0, label1, uwb_0, anchor_id + M5.update() + + +if __name__ == "__main__": + try: + setup() + while True: + loop() + except (Exception, KeyboardInterrupt) as e: + try: + from utility import print_error_msg + + print_error_msg(e) + except ImportError: + print("please update to latest firmware") diff --git a/m5stack/Makefile b/m5stack/Makefile index 08eb3acb..a45e0df2 100644 --- a/m5stack/Makefile +++ b/m5stack/Makefile @@ -282,6 +282,7 @@ patch: $(call Package/patche,$(abspath ./components/lv_bindings),$(abspath ./patches/0002_avoid_lv_bindings_compile_error.patch)) $(call Package/patche,$(abspath ./../micropython),$(abspath ./patches/0003-modtime-Add-timezone.patch)) $(call Package/patche,$(abspath $(IDF_PATH)),$(abspath ./patches/1000-WIP-Compatible-with-esp-adf-v2.6.patch)) + $(call Package/patche,$(abspath $(IDF_PATH)),$(abspath ./patches/1001-Fix-I2C-timeout.patch)) $(call Package/patche,$(abspath ./components/M5Unified/M5Unified),$(abspath ./patches/2003-Support-LTR553.patch)) $(call Package/patche,$(abspath ./components/M5Unified/M5Unified),$(abspath ./patches/2004-Support-AtomS3R.patch)) $(call Package/patche,$(abspath $(ADF_PATH)),$(abspath ./patches/3000-commponents-audio_board-Add-ESP32_S3_BOX_3-board.patch)) @@ -291,6 +292,7 @@ patch: unpatch: $(call Package/unpatche,$(abspath ./components/lv_bindings),$(abspath ./patches/0002_avoid_lv_bindings_compile_error.patch)) $(call Package/unpatche,$(abspath ./../micropython),$(abspath ./patches/0003-modtime-Add-timezone.patch)) + $(call Package/unpatche,$(abspath $(IDF_PATH)),$(abspath ./patches/1001-Fix-I2C-timeout.patch)) $(call Package/unpatche,$(abspath $(IDF_PATH)),$(abspath ./patches/1000-WIP-Compatible-with-esp-adf-v2.6.patch)) $(call Package/unpatche,$(abspath ./components/M5Unified/M5Unified),$(abspath ./patches/2004-Support-AtomS3R.patch)) $(call Package/unpatche,$(abspath ./components/M5Unified/M5Unified),$(abspath ./patches/2003-Support-LTR553.patch)) diff --git a/m5stack/board.cpp b/m5stack/board.cpp index 9f02bc00..0833ffe3 100644 --- a/m5stack/board.cpp +++ b/m5stack/board.cpp @@ -9,28 +9,53 @@ extern "C" { #include "uiflow_utility.h" #include + #include "esp_log.h" - void board_init() + void in_i2c_init(void) { - auto cfg = M5.config(); - cfg.output_power = false; - M5.begin(cfg); - if (M5.getBoard() == m5::board_t::board_M5StackCoreS3 || M5.getBoard() == m5::board_t::board_M5StackCoreS3SE) { - periph_module_disable(PERIPH_I2C1_MODULE); + gpio_num_t in_scl = (gpio_num_t)M5.getPin(m5::pin_name_t::in_i2c_scl); + gpio_num_t in_sda = (gpio_num_t)M5.getPin(m5::pin_name_t::in_i2c_sda); + gpio_num_t ex_scl = (gpio_num_t)M5.getPin(m5::pin_name_t::ex_i2c_scl); + gpio_num_t ex_sda = (gpio_num_t)M5.getPin(m5::pin_name_t::ex_i2c_sda); + i2c_port_t ex_port = I2C_NUM_0; +#if SOC_I2C_NUM == 1 + i2c_port_t in_port = I2C_NUM_0; +#else + i2c_port_t in_port = I2C_NUM_1; + if (in_scl == ex_scl && in_sda == ex_sda) { + in_port = ex_port; + } +#endif + + if (in_scl != 255 || in_sda != 255) { + ESP_LOGW("BOARD", "I2C1 init"); + if (in_port == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else { + periph_module_enable(PERIPH_I2C1_MODULE); + } i2c_config_t conf; memset(&conf, 0, sizeof(i2c_config_t)); conf.mode = I2C_MODE_MASTER; - conf.sda_io_num = GPIO_NUM_12; + conf.sda_io_num = in_sda; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; - conf.scl_io_num = GPIO_NUM_11; + conf.scl_io_num = in_scl; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = 100000; // .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */ - i2c_param_config(I2C_NUM_1, &conf); - i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0); + i2c_param_config(in_port, &conf); + i2c_driver_install(in_port, I2C_MODE_MASTER, 0, 0, 0); } } + void board_init() + { + auto cfg = M5.config(); + cfg.output_power = false; + M5.begin(cfg); + in_i2c_init(); + } + void power_init() { char power_mode[32] = {0}; diff --git a/m5stack/components/M5Unified/mpy_m5unified.cpp b/m5stack/components/M5Unified/mpy_m5unified.cpp index cb293148..b88498b8 100644 --- a/m5stack/components/M5Unified/mpy_m5unified.cpp +++ b/m5stack/components/M5Unified/mpy_m5unified.cpp @@ -362,23 +362,14 @@ mp_obj_t m5_begin(size_t n_args, const mp_obj_t *args) { // initial M5.begin(cfg); - // if (M5.getBoard() == m5::board_t::board_M5StackCoreS3) { - // periph_module_disable(PERIPH_I2C1_MODULE); - // i2c_config_t conf; - // memset(&conf, 0, sizeof(i2c_config_t)); - // conf.mode = I2C_MODE_MASTER; - // conf.sda_io_num = GPIO_NUM_12; - // conf.sda_pullup_en = GPIO_PULLUP_ENABLE; - // conf.scl_io_num = GPIO_NUM_11; - // conf.scl_pullup_en = GPIO_PULLUP_ENABLE; - // conf.master.clk_speed = 100000; - // // .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */ - // esp_err_t ret = i2c_param_config(I2C_NUM_1, &conf); - // ESP_LOGE("*", "i2c_param_config: %d", ret); - // ret = i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0); - // ESP_LOGE("*", "i2c_driver_install: %d", ret); - // } + // if (M5.getBoard() != m5::board_t::board_M5StackCoreS3 + // && M5.getBoard() != m5::board_t::board_M5StackCoreS3SE + // && M5.getBoard() != m5::board_t::board_M5StackCore2 + // && M5.getBoard() != m5::board_t::board_M5Tough + // && M5.getBoard() != m5::board_t::board_M5AtomS3 + // ) { M5.In_I2C.release(); + // } M5.Display.clear(); // default display diff --git a/m5stack/libs/module/__init__.py b/m5stack/libs/module/__init__.py index 417da980..74f5475d 100644 --- a/m5stack/libs/module/__init__.py +++ b/m5stack/libs/module/__init__.py @@ -9,16 +9,21 @@ "DisplayModule": "display", "DualKmeterModule": "dual_kmeter", "Encoder4MotorModule": "encoder4_motor", + "GRBLModule": "grbl", "HMIModule": "hmi", "IotBaseCatmModule": "iot_base_catm", "LoraModule": "lora", + "LoRaWANModule": "lorawan", + "LoRaWAN868Module": "lorawan868", "Module4In8Out": "module_4in8out", + "NBIOTModule": "nbiot", "PLUSModule": "plus", "PPSModule": "pps", "RCAModule": "rca", - "Relay4Module": "relay_4", "Relay2Module": "relay_2", + "Relay4Module": "relay_4", "RS232Module": "rs232", + "StepMotorDriverModule": "step_motor_driver", } # Lazy loader, effectively does: diff --git a/m5stack/libs/module/grbl.py b/m5stack/libs/module/grbl.py new file mode 100644 index 00000000..1983ee35 --- /dev/null +++ b/m5stack/libs/module/grbl.py @@ -0,0 +1,277 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +from .mbus import i2c1 +from .module_helper import ModuleError +from micropython import const +import re +import math +import time + + +class GRBLModule: + """ + + note: + cn: GRBL 13.2 是 M5Stack 堆叠模块系列中的三轴步进电机驱动器模块。它采用 ATmega328P-AU 控制器,带有三组 DRV8825PWPR 步进电机驱动芯片控制方式,可以同时驱动三个双极步进电机。 + en: GRBL 13.2 is a three-axis stepper motor driver module in the M5Stack stacking module series. It uses an ATmega328P-AU controller with three sets of DRV8825PWPR stepper motor driver chip control ways, which can drive three bipolar steppers at the same time. + + details: + color: "#0FE6D7" + link: https://docs.m5stack.com/en/module/grbl13.2 + image: https://static-cdn.m5stack.com/resource/docs/products/module/grbl13.2/grbl13.2_01.webp + category: Module + + example: + - ../../../examples/module/grbl_example.py + + m5f2: + - module/grbl_example.m5f2 + + + """ + + """ + constant: Motor mode + """ + MODE_ABSOLUTE = const(0) + MODE_RELATIVE = const(1) + + def __init__( + self, + address: int = 0x70, + ): + """ + note: + en: Initialize the GRBLModule. + cn: 初始化GRBL模块。 + + params: + address: + note: The I2C address of the device. + """ + + self.i2c = i2c1 + self.addr = address + + # Check if the devices are connected and accessible + if self.addr not in self.i2c.scan(): + raise ModuleError("GRBLModule not found at I2C address 0x%02X" % self.addr) + + self.mode = self.MODE_ABSOLUTE + self.last_speed = 300 + + def g_code(self, command): + """ + note: + en: Send the G-code command. + cn: 发送G代码命令。 + + params: + command: + note: The G-code command. + """ + self.i2c.writeto(self.addr, command + "\n") + return self.get_code_time(command) + + def get_code_time(self, code) -> int: + """ + note: + en: Get the time of the code. + cn: 获取代码的时间。 + + params: + code: + note: The G-code command + + return: + note: The estimated time of the command. + + """ + x_value = re.search(r"X-*\d+", code) + y_value = re.search(r"Y-*\d+", code) + z_value = re.search(r"Z-*\d+", code) + speed = re.search(r"F\d+", code) + hold = re.search(r"P\d+", code) + hold = float(hold.group(0)[1:]) * 1000 if hold else 0 + x_value = int(x_value.group(0)[1:]) if x_value else 0 + y_value = int(y_value.group(0)[1:]) if y_value else 0 + z_value = int(z_value.group(0)[1:]) if z_value else 0 + self.last_speed = int(speed.group(0)[1:]) if speed else self.last_speed + time = ( + math.sqrt(x_value * x_value + y_value * y_value + z_value * z_value) + * 60 + * 1000 + / self.last_speed + ) + time += hold + return int(time) + + def turn(self, x=None, y=None, z=None, speed=None) -> int: + """ + note: + en: Turn the motor to a specific position. + cn: 将电机转到特定位置。 + + params: + x: + note: The position of the X motor, 1.6=360°. + y: + note: The position of the Y motor, 1.6=360°. + z: + note: The position of the Z motor, 1.6=360°. + speed: + note: The speed of the motor. + """ + command = "G1" + command += " X{}".format(x) if x is not None else "" + command += " Y{}".format(y) if y is not None else "" + command += " Z{}".format(z) if z is not None else "" + command += " F{}".format(speed) if speed else "" + self.last_speed = speed if speed else self.last_speed + return self.g_code(command) + + def set_mode(self, mode): + """ + note: + en: Set the mode of the motor. + cn: 设置电机的模式。 + + params: + mode: + note: The mode of the motor. + field: dropdown + options: + Absolute: GRBLModule.MODE_ABSOLUTE + Relative: GRBLModule.MODE_RELATIVE + """ + self.mode = mode + if mode == self.MODE_ABSOLUTE: + return self.g_code("G90") + else: + return self.g_code("G91") + + def init(self, x_step=None, y_step=None, z_step=None, acc=None): + """ + note: + en: Initialize the motor. + cn: 初始化电机。 + + params: + x_step: + note: The step of the X motor. + y_step: + note: The step of the Y motor. + z_step: + note: The step of the Z motor. + acc: + note: The acceleration of the motor. + """ + if x_step: + self.i2c.writeto(self.addr, "$0={}\n".format(x_step)) + time.sleep(0.01) + if y_step: + self.i2c.writeto(self.addr, "$1={}\n".format(y_step)) + time.sleep(0.01) + if z_step: + self.i2c.writeto(self.addr, "$2={}\n".format(z_step)) + time.sleep(0.01) + if acc: + self.i2c.writeto(self.addr, "$8={}\n".format(acc)) + + def flush(self): + """ + note: + en: Flush the buffer. + cn: 清空缓冲区。 + """ + while True: + data = self.i2c.readfrom(self.addr, 10) + if data[-1] == 255: + break + + def get_message(self) -> str: + """ + note: + en: Get the message. + cn: 获取消息。 + return: + note: The message string. + """ + i2c_data = "" + while True: + data = self.i2c.readfrom(self.addr, 10) + if data[-1] == 255: + i2c_data += data[: data.find(b"\x00")].decode() + break + i2c_data += data.decode() + return i2c_data + + def get_status(self) -> str: + """ + note: + en: Get the status. + cn: 获取状态。 + return: + note: The status string. + """ + self.flush() + self.i2c.writeto(self.addr, "@") + return self.get_message() + + def get_idle_state(self) -> bool: + """ + note: + en: Get the idle state. + cn: 获取空闲状态。 + return: + note: The idle state. + """ + return self.get_status()[0] == "I" + + def get_lock_state(self) -> bool: + """ + note: + en: Get the lock state. + cn: 获取锁定状态。 + return: + note: The lock state. + """ + return self.get_status()[0] == "A" + + def wait_idle(self): + """ + note: + en: Wait until the motor is idle. + cn: 等待电机空闲。 + """ + self.flush() + while not self.get_idle_state(): + time.sleep(0.1) + + def unlock_alarm_state(self): + """ + note: + en: Unlock the alarm state. + cn: 解锁报警状态。 + """ + self.i2c.writeto(self.addr, b"\x18") + time.sleep(0.01) + self.i2c.writeto(self.addr, "$X\r\n") + + def lock(self): + """ + note: + en: Lock the motor. + cn: 锁定电机。 + """ + self.g_code("$7=255") + + def unlock(self): + """ + note: + en: Unlock the motor. + cn: 解锁电机。 + """ + self.g_code("$7=25") diff --git a/m5stack/libs/module/lorawan.py b/m5stack/libs/module/lorawan.py new file mode 100644 index 00000000..2d57c4d1 --- /dev/null +++ b/m5stack/libs/module/lorawan.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +from unit.lorawan import LoRaWANUnit as LoRaWANModule diff --git a/m5stack/libs/module/lorawan868.py b/m5stack/libs/module/lorawan868.py new file mode 100644 index 00000000..225d13bd --- /dev/null +++ b/m5stack/libs/module/lorawan868.py @@ -0,0 +1,585 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +from micropython import const +import time +import machine + + +class LoRaWAN868Module: + """ + + note: + cn: COM.LoRaWAN是M5Stack堆叠模块系列中的LoRaWAN通信模块,支持节点到节点或LoRaWAN通信。 + en: COM.LoRaWAN is a LoRaWAN communication module in the M5Stack stackable module series, supporting node-to-node or LoRaWAN communication. + + details: + color: "#0FE6D7" + link: https://docs.m5stack.com/en/module/comx_lorawan + image: https://static-cdn.m5stack.com/resource/docs/products/module/comx_lorawan/comx_lorawan_01.webp + category: Module + + example: + - ../../../examples/module/lorawan868_example_tx.py + - ../../../examples/module/lorawan868_example_rx.py + + m5f2: + - module/lorawan868_example_tx.m5f2 + - module/lorawan868_example_rx.m5f2 + + """ + + """ + constant: LoRa band frequency + """ + BAND_470 = const("470000000") + BAND_868 = const("868000000") + BAND_915 = const("915000000") + + """ + constant: LoRa Mode + """ + MODE_LORA = const("0") + MODE_LORAWAN = const("1") + + def __init__(self, id=1, port=None, band=BAND_868): + """ + note: + en: Initialize the LoRaWANModule. + cn: 初始化LoRaWANModule。 + + params: + id: + note: The UART ID to use for communication. + port: + note: The UART port to use for communication, specified as a tuple of (rx, tx) pins. + timeout: + note: The timeout for the UART communication. + band: + note: The frequency to use for LoRa communication + options: + 470MHz: LoRaWANModule.BAND_470 + 868MHz: LoRaWANModule.BAND_868 + 915MHz: LoRaWANModule.BAND_915 + """ + self.band = band + self.uart = machine.UART(id, tx=port[1], rx=port[0]) + self.uart.init(115200, bits=0, parity=None, stop=1, rxbuf=1024) + + self.wake_up() + self.set_mode(self.MODE_LORA) + self.set_parameters(self.band, 10, 7, 0, 1, 8, 1, 0, 0) + ret = self.at_cmd("PrintMode", "0") # String Output Mode + + if not ret: + raise Exception("LoRaWAN module not detected") + self.flush() + + def set_mode(self, mode): + """ + note: + en: Set the mode of the LoRaWAN module. + cn: 设置LoRaWAN模块的模式。 + + params: + mode: + note: The mode to set. + options: + LoRa: LoRaWANModule.MODE_LORA + LoRaWAN: LoRaWANModule.MODE_LORAWAN + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("LORAWAN", mode) + + def set_parameters(self, freq, power, sf, bw, cr, preamble, crc, iq_inv, save): + """ + note: + en: Set the parameters of the LoRaWAN module. + cn: 设置LoRaWAN模块的参数。 + + params: + freq: + note: Set LoRa listening/sending frequency in Hz. + power: + note: LoRa signal output power in dBm; + sf: + note: Spreading factor, from 5~12 + bw: + note: Bandwidth 0 – 125K, 1 – 250K, 2 – 500K; + cr: + note: 1 – 4/5, 2 – 4/6, 3 – 4/7, 4 – 4/8; + preamble: + note: Preamble Length from 8~65535 bit; + crc: + note: 0 – disable CRC check, 1 – enable CRC check; + iq_inv: + note: 0 -- not inverted, 1 – inverted; + save: + note: Save parameters to FLASH, 0 – not save, 1 – save. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd( + "LoraSet", + "{},{},{},{},{},{},{},{},{}".format( + freq, power, sf, bw, cr, preamble, crc, iq_inv, save + ), + ) + + def wake_up(self): + """ + note: + en: Wake up the device through a serial port interrupt. After resetting, the device is in sleep state. + In theory, sending any data through the serial port can trigger the interrupt and wake up the device. + cn: 通过串口中断唤醒设备。设备重置后进入睡眠状态,通过串口发送任意数据即可唤醒设备。 + + example: + Send any string like "ABC" to wake up the device. + """ + self.at_cmd("XXX") # XXX可以是任意数据,比如"ABC" + + def sleep(self): + """ + note: + en: Put the device into low-power mode. + cn: 将设备置于低功耗模式。 + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("LPM", 1) + + def reset(self): + """ + note: + en: Reset the device. + cn: 重置设备。 + """ + self.at_cmd("RESET", 1) + + def restore_factory_settings(self): + """ + note: + en: Restore the device to factory settings. The parameters will reset and the device will enter sleep mode after response ends. + cn: 恢复设备至出厂设置。恢复后,设备将重置参数并在响应结束后进入睡眠模式。 + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("DefaultSet", 1) + + def set_copyright(self, enable=True): + """ + note: + en: Enable or disable copyright information print when boot loader mode begins. Default is enable. + cn: 启用或禁用在启动加载器模式下的版权信息打印。默认启用。 + + params: + enable: + note: Set True to enable, False to disable. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("Copyright", 1 if enable else 0) + + def set_auto_low_power(self, enable=True): + """ + note: + en: Enable or disable automatic low-power mode. Default is enable. + cn: 启用或禁用自动低功耗模式。默认启用。 + + params: + enable: + note: Set True to enable, False to disable. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("AutoLPM", 1 if enable else 0) + + def query_chip_id(self): + """ + note: + en: Query the unique ID of the chip, which can be used to query the corresponding serial number. + cn: 查询芯片的唯一ID,可用于查询相应的序列号。 + + returns: | + The unique chip ID. + """ + return self.at_query("ChipID") + + def enable_rx(self, timeout=0): + """ + note: + en: Enable the LoRaWAN module to receive data. + cn: 启用LoRaWAN模块接收数据。 + + params: + timeout: + note: The timeout for the receive operation. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("RX", str(timeout)) + + def set_deveui(self, deveui=None): + """ + note: + en: Set or query the DevEui. DevEui must be 16 hex characters (0-9, A-F). + cn: 设置或查询DevEui。DevEui必须是16位十六进制字符(0-9, A-F)。 + + params: + deveui: + note: The DevEui to set. If None, query the current DevEui. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("DevEui", deveui) + + def set_appeui(self, appeui=None): + """ + note: + en: Set or query the AppEui. AppEui must be 16 hex characters (0-9, A-F). + cn: 设置或查询AppEui。AppEui必须是16位十六进制字符(0-9, A-F)。 + + params: + appeui: + note: The AppEui to set. If None, query the current AppEui. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("AppEui", appeui) + + def set_appkey(self, appkey=None): + """ + note: + en: Set or query the AppKey. AppKey must be 32 hex characters (0-9, A-F). + cn: 设置或查询AppKey。AppKey必须是32位十六进制字符(0-9, A-F)。 + + params: + appkey: + note: The AppKey to set. If None, query the current AppKey. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("AppKey", appkey) + + def set_nwkskey(self, nwkskey=None): + """ + note: + en: Set or query the NwkSKey. NwkSKey must be 32 hex characters (0-9, A-F). + cn: 设置或查询NwkSKey。NwkSKey必须是32位十六进制字符(0-9, A-F)。 + + params: + nwkskey: + note: The NwkSKey to set. If None, query the current NwkSKey. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("NwkSKey", nwkskey) + + def set_appskey(self, appskey=None): + """ + note: + en: Set or query the AppSKey. AppSKey must be 32 hex characters (0-9, A-F). + cn: 设置或查询AppSKey。AppSKey必须是32位十六进制字符(0-9, A-F)。 + + params: + appskey: + note: The AppSKey to set. If None, query the current AppSKey. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("AppSKey", appskey) + + def set_devaddr(self, devaddr=None): + """ + note: + en: Set or query the DevAddr. DevAddr must be 8 hex characters (0-9, A-F). + cn: 设置或查询DevAddr。DevAddr必须是8位十六进制字符(0-9, A-F)。 + + params: + devaddr: + note: The DevAddr to set. If None, query the current DevAddr. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("DevAddr", devaddr) + + def set_otaa_mode(self, enable=True): + """ + note: + en: Set or query the OTAA mode. 1 for OTAA mode, 0 for ABP mode. + cn: 设置或查询OTAA模式。1表示OTAA模式,0表示ABP模式。 + + params: + enable: + note: Set True for OTAA mode, False for ABP mode. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("OTAA", 1 if enable else 0) + + def set_adr(self, enable=True): + """ + note: + en: Enable or disable the ADR (Adaptive Data Rate) function. Default is enabled. + cn: 启用或禁用ADR(自适应数据速率)功能。默认启用。 + + params: + enable: + note: Set True to enable ADR, False to disable. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("ADR", 1 if enable else 0) + + def set_channel_mask(self, mask): + """ + note: + en: Set or query the LoRaWAN working channel mask. + cn: 设置或查询LoRaWAN的工作频道掩码。 + + params: + mask: + note: The channel mask in hexadecimal format, e.g., 0000000000000000000000FF for channels 0~7. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("ChMask", mask) + + def join_network(self): + """ + note: + en: Join the network using OTAA (Over-The-Air Activation). This command triggers the join process. + cn: 使用OTAA(空中激活)加入网络。该命令触发入网过程。 + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("Join", 1) + + def set_duty_cycle(self, cycle): + """ + note: + en: Set or query the communication cycle in milliseconds. For example, 60000 means communication every 60 seconds. + cn: 设置或查询通信周期,单位为毫秒。例如,60000表示每60秒通信一次。 + + params: + cycle: + note: The communication cycle in milliseconds. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("DutyCycle", cycle) + + def set_class_mode(self, mode): + """ + note: + en: Set or query the device's communication mode. Only Class A or Class C are valid. + cn: 设置或查询设备的通信模式。只能设置为Class A或Class C。 + + params: + mode: + note: Set "A" for Class A or "C" for Class C. + + rreturns: + True if the command is successful, False if not. + """ + return self.at_cmd("Class", mode) + + def set_ack(self, enable=True): + """ + note: + en: Enable or disable the ACK receipt function. If enabled, the device waits for acknowledgment from the gateway. + cn: 启用或禁用ACK确认功能。如果启用,设备将等待网关的确认。 + + params: + enable: + note: Set True to enable ACK, False to disable. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("IsTxConfirmed", 1 if enable else 0) + + def set_app_port(self, port): + """ + note: + en: Set or query the application port (fport) for upstream data. Valid range is 0~255. + cn: 设置或查询上行数据端口(fport)。有效范围是0~255。 + + params: + port: + note: The application port to set. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("AppPort", port) + + def set_retransmission_count(self, count=None): + """ + note: + en: Set or query the number of retransmissions if communication fails. The valid range is 3~8. + cn: 设置或查询在通信失败时的重传次数。有效范围是3~8。 + + params: + count: + note: The number of retransmissions to set. If None, query the current setting. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("ConfirmedNbTrials", count) + + def send_hex(self, hex_data): + """ + note: + en: Send hex data in LoRaWAN or LoRa mode. Hex characters must be in pairs (e.g., "AABB"). + cn: 在LoRaWAN或LoRa模式下发送十六进制数据。十六进制字符必须成对出现(例如:"AABB")。 + + params: + hex_data: + note: The hex data to send, up to 64 bytes. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("SendHex", hex_data) + + def send_string(self, string_data): + """ + note: + en: Send string data in LoRaWAN or LoRa mode. The string must consist of ASCII characters. + cn: 在LoRaWAN或LoRa模式下发送字符串数据。字符串必须由ASCII字符组成。 + + params: + string_data: + note: The string data to send, up to 64 bytes. + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("SendStr", string_data) + + def query_lorawan_mode(self): + """ + note: + en: Query if the device is in LoRaWAN or normal LoRa mode. + cn: 查询设备是处于LoRaWAN模式还是普通LoRa模式。 + + returns: | + +LORAWAN: 1 if in LoRaWAN mode, 0 if in normal LoRa mode. + """ + return self.at_query("LORAWAN") + + def save_parameters_to_flash(self): + """ + note: + en: Save the current LoRa parameters to FLASH memory. + cn: 将当前LoRa参数保存到FLASH存储中。 + + returns: + True if the command is successful, False if not. + """ + return self.at_cmd("SaveToFLASH", 1) + + def at_cmd(self, cmd, data=None): + """ + note: + en: Send an AT command to the LoRaWAN module. + cn: 向LoRaWAN模块发送AT命令。 + + params: + cmd: + note: The AT command to send. + data: + note: The data to send with the AT command. + + returns: | + note: True if the command is successful, False if not. + """ + if data: + self.uart.write("AT+{}={}".format(cmd, str(data))) + else: + self.uart.write("AT+{}".format(cmd)) + time.sleep(0.1) + ret = self.uart.read() + if "ERROR" in ret: + return False + return True + + def at_query(self, cmd): + """ + note: + en: Query the current settings of the LoRaWAN module. + cn: 查询LoRaWAN模块的当前设置。 + + params: + cmd: + note: The AT command to query. + + returns: | + note: The response from the LoRaWAN module. + """ + self.uart.write("AT+{}=?".format(cmd)) + time.sleep(0.1) + return self.uart.read() + + def at_receive(self): + """ + note: + en: Receive a response from the LoRaWAN module. + cn: 从LoRaWAN模块接收响应。 + + returns: | + note: The response from the LoRaWAN module. + """ + return self.uart.read() + + def flush(self): + """ + note: + en: Clear the UART buffer. + cn: 清空UART缓冲区。 + """ + self.uart.flush() + + def any(self): + """ + note: + en: Check if there is any data in the UART buffer. + cn: 检查UART缓冲区是否有数据。 + + returns: | + note: True if there is data, False if not. + """ + return self.uart.any() + + def receive_data(self): + """ + note: + en: Receive data from the LoRaWAN module. + cn: 从LoRaWAN模块接收数据。 + + returns: | + note: The received data. + """ + return self.uart.read() diff --git a/m5stack/libs/module/manifest.py b/m5stack/libs/module/manifest.py index 3c7bf96b..6ba43b9f 100644 --- a/m5stack/libs/module/manifest.py +++ b/m5stack/libs/module/manifest.py @@ -10,18 +10,23 @@ "display.py", "dual_kmeter.py", "encoder4_motor.py", + "grbl.py", "hmi.py", "iot_base_catm.py", "lora.py", + "lorawan.py", + "lorawan868.py", "mbus.py", "module_4in8out.py", "module_helper.py", + "nbiot.py", "plus.py", "pps.py", "rca.py", - "relay_4.py", "relay_2.py", + "relay_4.py", "rs232.py", + "step_motor_driver.py", ), base_path="..", opt=0, diff --git a/m5stack/libs/module/mbus.py b/m5stack/libs/module/mbus.py index 86ad367b..a09281e8 100644 --- a/m5stack/libs/module/mbus.py +++ b/m5stack/libs/module/mbus.py @@ -14,6 +14,7 @@ M5.BOARD.M5Stack: MBusIO(2, 5, 21, 22, 18, 23, 19), M5.BOARD.M5StackCore2: MBusIO(32, 33, 21, 22, 18, 23, 38), M5.BOARD.M5StackCoreS3: MBusIO(2, 1, 12, 11, 36, 37, 35), + M5.BOARD.M5Tough: MBusIO(32, 33, 21, 22, 18, 23, 38), }.get(M5.getBoard()) diff --git a/m5stack/libs/module/nbiot.py b/m5stack/libs/module/nbiot.py new file mode 100644 index 00000000..b925ae62 --- /dev/null +++ b/m5stack/libs/module/nbiot.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +import machine +from driver.simcom.sim7020 import SIM7020 +import sys + +if sys.platform != "esp32": + from typing import Literal + + +class NBIOTModule(SIM7020): + def __init__(self, id: Literal[0, 1, 2], tx: int, rx: int) -> None: + self.uart = machine.UART( + id, tx=tx, rx=rx, baudrate=115200, bits=8, parity=None, stop=1, rxbuf=1024 + ) + super().__init__(uart=self.uart) + if not self.check_modem_is_ready(): + raise Exception("NBIOT module not found in mbus") + self.set_command_echo_mode(0) diff --git a/m5stack/libs/module/step_motor_driver.py b/m5stack/libs/module/step_motor_driver.py new file mode 100644 index 00000000..6c4f4bf6 --- /dev/null +++ b/m5stack/libs/module/step_motor_driver.py @@ -0,0 +1,313 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +from .mbus import i2c1 +from .module_helper import ModuleError +import struct +from micropython import const +from machine import Pin, PWM + + +class StepMotorDriverModule: + """ + + note: + cn: 步进电机驱动模块 13.2 V1.1 是一款适配于 M5 主控的步进电机驱动器,采用 STM32+HR8825 步进电机驱动方案,提供 3 路双极步进电机控制接口。 + en: StepMotor Driver Module 13.2 V1.1 is a stepper motor driver adapted to M5 main control, using STM32+HR8825 stepper motor drive scheme, providing 3-way bipolar stepper motor control interface. + + details: + color: "#0FE6D7" + link: https://docs.m5stack.com/en/module/Stepmotor%20Driver%20Module13.2%20v1.1 + image: https://static-cdn.m5stack.com/resource/docs/products/module/Stepmotor%20Driver%20Module13.2%20v1.1/img-c2b8ceac-b6be-4cec-9a15-228fcf8623e7.webp + category: Module + + example: | + from module import StepMotorDriverModule + motor = StepMotorDriverModule(step_pin=(16, 12, 15), dir_pin=(17, 13, 0)) # Core + + + """ + + """ + constant: Motor IDs + """ + MOTOR_X = const(0) + MOTOR_Y = const(1) + MOTOR_Z = const(2) + + """ + constant: Motor states + """ + MOTOR_STATE_ENABLE = const(1) + MOTOR_STATE_DISABLE = const(0) + + """ + constant: Register addresses + """ + INPUT_REG = const(0x00) + OUTPUT_REG = const(0x01) + POLINV_REG = const(0x02) + CONFIG_REG = const(0x03) + FAULT_REG = const(0x04) + RESET_REG = const(0x05) + FIRM_REG = const(0xFE) + I2C_REG = const(0xFF) + + """ + constant: Microstep values + """ + STEP_FULL = const(0x00) + STEP1_2 = const(0x04) + STEP1_4 = const(0x02) + STEP1_8 = const(0x06) + STEP1_16 = const(0x01) + STEP1_32 = const(0x07) + + def __init__( + self, + address: int = 0x27, + step_pin: tuple | list = (16, 12, 15), + dir_pin: tuple | list = (17, 13, 0), + ): + """ + note: + en: Initialize the StepMotorDriverModule. + cn: 初始化步进电机驱动模块。 + + params: + address: + note: The I2C address of the device. + step_pin: + note: The step pin (X, Y, Z) of the motor. + dir_pin: + note: The dir pin (X, Y, Z) of the motor. + """ + + self.i2c = i2c1 + self.addr = address + + # Check if the devices are connected and accessible + if self.addr not in self.i2c.scan(): + raise ModuleError("StepMotorDriverModule not found at I2C address 0x%02X" % self.addr) + + self.set_motor_state(self.MOTOR_STATE_DISABLE) + self.reset_motor(self.MOTOR_X, 0) + self.reset_motor(self.MOTOR_Y, 0) + self.reset_motor(self.MOTOR_Z, 0) + self.set_microstep(self.STEP_FULL) + self.pwm_x = PWM(step_pin[0], freq=500, duty=50) + self.pwm_y = PWM(step_pin[1], freq=500, duty=50) + self.pwm_z = PWM(step_pin[2], freq=500, duty=50) + self.dir_x = Pin(dir_pin[0], Pin.OUT, value=1) + self.dir_y = Pin(dir_pin[1], Pin.OUT, value=1) + self.dir_z = Pin(dir_pin[2], Pin.OUT, value=1) + + def reset_motor(self, motor_id, state: bool = False): + """ + note: + en: Reset the motor. + cn: 重置电机。 + + params: + motor_id: + note: The motor to reset. + field: dropdown + options: + X: StepMotorDriverModule.MOTOR_X + Y: StepMotorDriverModule.MOTOR_Y + Z: StepMotorDriverModule.MOTOR_Z + state: + note: The state of the motor. + field: switch + """ + rdata = self.i2c.readfrom_mem(self.addr, self.RESET_REG, 1) + rdata = struct.unpack("B", rdata)[0] + if state: + rdata &= ~(0x01 << motor_id) + else: + rdata |= 0x01 << motor_id + self.i2c.writeto_mem(self.addr, self.RESET_REG, struct.pack("B", rdata)) + + def set_motor_state(self, state: bool = False): + """ + note: + en: Enable or disable the motor. + cn: 启用或禁用电机。 + + params: + state: + note: The state of the motor. + field: switch + """ + rdata = self.i2c.readfrom_mem(self.addr, self.OUTPUT_REG, 1) + rdata = struct.unpack("B", rdata)[0] + rdata &= 0xEF + if state == 0: + rdata |= 0x10 + self.i2c.writeto_mem(self.addr, self.OUTPUT_REG, struct.pack("B", rdata)) + + def set_microstep(self, step): + """ + note: + en: Set the microstep. + cn: 设置微步。 + + params: + step: + note: The microstep value. + field: dropdown + options: + FULL: StepMotorDriverModule.STEP_FULL + 1/2: StepMotorDriverModule.STEP1_2 + 1/4: StepMotorDriverModule.STEP1_4 + 1/8: StepMotorDriverModule.STEP1_8 + 1/16: StepMotorDriverModule.STEP1_16 + 1/32: StepMotorDriverModule.STEP1_32 + """ + rdata = self.i2c.readfrom_mem(self.addr, self.OUTPUT_REG, 1) + rdata = struct.unpack("B", rdata)[0] + rdata &= 0x1F + rdata |= step << 5 + self.i2c.writeto_mem(self.addr, self.CONFIG_REG, struct.pack("B", rdata)) + + def set_motor_pwm_freq(self, motor_id, freq: int): + """ + note: + en: Set the motor pwm freq. + cn: 设置电机PWM频率。 + + params: + motor_id: + note: The motor to set the freq. + field: dropdown + options: + X: StepMotorDriverModule.MOTOR_X + Y: StepMotorDriverModule.MOTOR_Y + Z: StepMotorDriverModule.MOTOR_Z + freq: + note: The freq value. + """ + if motor_id == self.MOTOR_X: + self.pwm_x.freq(freq) + elif motor_id == self.MOTOR_Y: + self.pwm_y.freq(freq) + elif motor_id == self.MOTOR_Z: + self.pwm_z.freq(freq) + + def set_motor_direction(self, motor_id, direction: bool): + """ + note: + en: Set the motor direction. + cn: 设置电机方向。 + + params: + motor_id: + note: The motor to set the direction. + field: dropdown + options: + X: StepMotorDriverModule.MOTOR_X + Y: StepMotorDriverModule.MOTOR_Y + Z: StepMotorDriverModule.MOTOR_Z + direction: + note: The direction value. + field: dropdown + options: + Positive: 1 + Negative: 0 + """ + if motor_id == self.MOTOR_X: + self.dir_x.value(direction) + elif motor_id == self.MOTOR_Y: + self.dir_y.value(direction) + elif motor_id == self.MOTOR_Z: + self.dir_z.value(direction) + + def get_all_limit_switch_state(self): + """ + note: + en: Get all io state. + cn: 获取所有IO状态。 + """ + rdata = self.i2c.readfrom_mem(self.addr, self.INPUT_REG, 1) + return struct.unpack("B", rdata)[0] & 0x0F + + def get_limit_switch_state(self, switch_id: int): + """ + note: + en: Get the io state. + cn: 获取IO状态。 + + params: + switch_id: + note: The io id. + """ + rdata = self.i2c.readfrom_mem(self.addr, self.INPUT_REG, 1) + return (struct.unpack("B", rdata)[0] >> switch_id) & 0x01 + + def get_fault_io_state(self, motor_id: int): + """ + note: + en: Get the fault io state. + cn: 获取故障IO状态。 + + params: + motor_id: + note: The motor id. + field: dropdown + options: + X: StepMotorDriverModule.MOTOR_X + Y: StepMotorDriverModule.MOTOR_Y + Z: StepMotorDriverModule.MOTOR_Z + """ + rdata = self.i2c.readfrom_mem(self.addr, self.FAULT_REG, 1) + return (struct.unpack("B", rdata)[0] >> motor_id) & 0x01 + + def motor_control(self, motor_id, state: bool): + """ + note: + en: Control the motor to rotate/stop. + cn: 控制电机旋转/停止。 + + params: + motor_id: + note: The motor id. + field: dropdown + options: + X: StepMotorDriverModule.MOTOR_X + Y: StepMotorDriverModule.MOTOR_Y + Z: StepMotorDriverModule.MOTOR_Z + state: + note: The state value. + field: dropdown + options: + Rotate: 1 + Stop: 0 + """ + target = [self.pwm_x, self.pwm_y, self.pwm_z][motor_id] + if state: + target.duty(50) + else: + target.duty(0) + + def get_firmware_version(self): + """ + note: + en: Get the firmware version. + cn: 获取固件版本。 + """ + rdata = self.i2c.readfrom_mem(self.addr, self.FIRM_REG, 1) + return struct.unpack("B", rdata)[0] + + def set_i2c_address(self, new_address: int): + """ + note: + en: Set the i2c address. + cn: 设置I2C地址。 + + params: + new_address: + note: The new address. + """ + self.i2c.writeto_mem(self.addr, self.I2C_REG, struct.pack("B", new_address)) + self.addr = new_address diff --git a/m5stack/libs/unit/__init__.py b/m5stack/libs/unit/__init__.py index 64264923..31c63851 100644 --- a/m5stack/libs/unit/__init__.py +++ b/m5stack/libs/unit/__init__.py @@ -94,6 +94,7 @@ "Relay2Unit": "relay2", "RFIDUnit": "rfid", "RGBUnit": "rgb", + "Roller485Unit": "roller485", "ISO485Unit": "rs485_iso", "RS485Unit": "rs485", "RTC8563Unit": "rtc8563", @@ -104,6 +105,7 @@ "SynthUnit": "synth", "ThermalUnit": "thermal", "Thermal2Unit": "thermal2", + "TimerPWRUnit": "timerpwr", "TMOSUnit": "tmos", "ToFUnit": "tof", "TOF4MUnit": "tof4m", diff --git a/m5stack/libs/unit/adc_v11.py b/m5stack/libs/unit/adc_v11.py index d7dccc87..24300469 100644 --- a/m5stack/libs/unit/adc_v11.py +++ b/m5stack/libs/unit/adc_v11.py @@ -27,7 +27,7 @@ def _available(self): raise UnitError("ADC V1.1 Unit not found in Grove") def get_voltage(self): - data = self.get_adc_raw_value + data = self.get_adc_raw_value() vol = ( data * 2.048 diff --git a/m5stack/libs/unit/manifest.py b/m5stack/libs/unit/manifest.py index 0159d011..2c18f82e 100644 --- a/m5stack/libs/unit/manifest.py +++ b/m5stack/libs/unit/manifest.py @@ -93,6 +93,7 @@ "relay4.py", "rfid.py", "rgb.py", + "roller485.py", "rs485_iso.py", "rs485.py", "rtc8563.py", @@ -103,6 +104,7 @@ "synth.py", "thermal.py", "thermal2.py", + "timerpwr.py", "tmos.py", "tof.py", "tof4m.py", @@ -121,5 +123,5 @@ "zigbee.py", ), base_path="..", - opt=3, + # opt=3, ) diff --git a/m5stack/libs/unit/roller485.py b/m5stack/libs/unit/roller485.py new file mode 100644 index 00000000..b6cdacc8 --- /dev/null +++ b/m5stack/libs/unit/roller485.py @@ -0,0 +1,819 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +from machine import I2C, UART +from micropython import const +from .pahub import PAHUBUnit +from .unit_helper import UnitError +import struct +import time +import sys + +if sys.platform != "esp32": + from typing import Literal + +#! I2C REGISTER MAP +_ROLLER485_I2C_ADDR = const(0x64) +_ROLLER485_RS485_ADDR = const(0x00) + +#! 485 VIA I2C CONTROL READ & WRITE !# +_READ_I2C_SLAVE_REG_ADDR = 0x60 +_WRITE_I2C_SLAVE_REG_ADDR = 0x61 +_READ_I2C_SLAVE_ADDR = 0x62 +_WRITE_I2C_SLAVE_ADDR = 0x63 + +_COMMON_REG = { + #! "COMMAND": (I2C-REGISTER, RS485-WRITE-REGISTER, RS485-WRITE-POSISTION, RS485-READ-REGISTER, RS485-READ-POSISTION, RS485-REGISTER-CONTINUOUS) + #! MOTOR CONFIGURATION REGISTER !# + "MOTOR_OUTPUT": (0x00, 0x00, 2, None, None, 0), + "MOTOR_MODE": (0x01, 0x01, 2, 0x40, 14, 0), + "MOTOR_ORP": (0x0A, 0x0E, 2, None, None, 0), + "STALL_PROTECT": (0x0B, 0x06, 6, None, None, 0), + "MOTOR_STATUS": (0x0C, None, None, 0x40, 15, 0), + "MOTOR_ERROR": (0x0D, None, None, 0x40, 16, 0), + "BTN_SWITCH_MODE": (0x0E, 0x09, 2, 0x43, 16, 0), + "MOTOR_JAM": (0x0F, 0x0D, 2, None, None, 0), + "MOTOR_ID": (0x10, 0x0C, 2, 0x43, 14, 0), + "BPS_RS485": (0x11, 0x0B, 2, 0x43, 15, 0), + "RGB_BRIGHT": (0x12, 0x0A, 6, 0x41, 15, 2), + #! SPEED CONTROL REGISTER !# + "SPEED_SETTING": (0x40, 0x20, 2, None, None, 1), + "SPEED_MAX_CURRENT": (0x50, 0x20, 6, None, None, 1), + "SPEED_READBACK": (0x60, None, None, 0x40, 2, 0), + "SPEED_PID": (0x70, 0x21, 2, 0x42, 2, 0), + #! POSITION CONTROL REGISTER !# + "POSITION_SETTING": (0x80, 0x22, 2, None, None, 1), + "POSITION_MAX_CURRENT": (0x20, 0x22, 6, None, None, 1), + "POSITION_READBACK": (0x90, None, None, 0x40, 6, 0), + "POSITION_PID": (0xA0, 0x23, 2, 0x43, 2, 0), + #! CURRENT CONTROL REGISTER !# + "MAX_CURRENT": (0xB0, 0x24, 2, None, None, 0), + "CURRENT_READBACK": (0xC0, None, None, 0x40, 10, 0), + #! SYSTEM REGISTER !# + "SYSTEM_RGB": (0x30, 0x0A, 2, 0x42, 14, 2), + "SYSTEM_RGB_MODE": (0x33, 0x0A, 5, 0x41, 14, 2), + "SYSTEM_VIN": (0x34, None, None, 0x41, 2, 0), + "SYSTEM_TEMP": (0x38, None, None, 0x41, 6, 0), + "SYSTEM_ENCODER": (0x3C, 0x08, 2, 0x41, 10, 0), + "SYSTEM_FLASH_WRITE": (0xF0, 0x07, 2, None, None, 0), + "SYSTEM_FIRMWARE": (0xFE, None, None, None, None, 0), + "SYSTEM_ADDRESS": (0xFF, None, None, None, None, 0), +} + + +class RollerBase: + #! Initialize the roller485Base. + def __init__(self) -> None: + pass + + def read(self, register, length) -> bytes: + raise NotImplementedError("Subclasses should implement this method!") + + def write(self, register, data: bytes) -> None: + raise NotImplementedError("Subclasses should implement this method!") + + def readfrom_mem(self, addr: int, mem_addr: int, nbytes: int) -> bytes: # 0x60 + raise NotImplementedError("Subclasses should implement this method!") + + def readfrom_mem_into(self, addr: int, mem_addr: int, buf: bytearray) -> None: # 0x60 + raise NotImplementedError("Subclasses should implement this method!") + + def writeto_mem(self, addr: int, mem_addr: int, buf: list) -> Literal[True]: # 0x61 + raise NotImplementedError("Subclasses should implement this method!") + + def readfrom(self, addr: int, nbytes: int): # 0x62 + raise NotImplementedError("Subclasses should implement this method!") + + def readfrom_into(self, addr: int, buf: bytearray) -> None: # 0x62 + raise NotImplementedError("Subclasses should implement this method!") + + def writeto(self, addr: int, buf: bytes | bytearray, stop: bool = True): + raise NotImplementedError("Subclasses should implement this method!") + + def scan(self) -> list: + raise NotImplementedError("Subclasses should implement this method!") + + def deinit(self) -> None: + raise NotImplementedError("Subclasses should implement this method!") + + #! MOTOR CONFIGURATION API !# + def set_motor_output_state(self, ctrl: int = 0) -> None: + """! Set the motor output state. + + @param ctrl: Control value for the motor output. + """ + self.write("MOTOR_OUTPUT", bytes([ctrl])) + + def get_motor_output_state(self) -> bool: + """! Get the motor output status. + + @return: True if the motor output is active, False otherwise. + """ + return self.read("MOTOR_OUTPUT", 1)[0] != 0 + + def set_motor_mode(self, mode: int) -> None: + """! Set the motor mode. + + @param mode: The mode to set for the motor. + """ + self.write("MOTOR_MODE", bytes([mode])) + + def get_motor_mode(self) -> int: + """! Get the motor mode. + + @return: The current motor mode. + """ + return self.read("MOTOR_MODE", 1)[0] + + def set_motor_over_range_protect_state(self, state: int = 1) -> None: + """! Set the motor over range protection state. + + @param state: Protection state value (1 to enable, 0 to disable). + """ + self.write("MOTOR_ORP", bytes([state])) + + def get_motor_over_range_protect_state(self) -> bool: + """! Get the motor over range protection status. + + @return: True if protection is enabled, False otherwise. + """ + return self.read("MOTOR_ORP", 1)[0] != 0 + + def remove_motor_jam_protect(self) -> None: + """! Set the motor jam release protection.""" + self.write("STALL_PROTECT", bytes([0x01])) + + def get_motor_status(self) -> int: + """! Get the motor status. + + @return: The current status of the motor. + """ + return self.read("MOTOR_STATUS", 1)[0] + + def get_motor_error_code(self) -> int: + """! Get the motor error code. + + @return: The current error code of the motor. + """ + return self.read("MOTOR_ERROR", 1)[0] + + def set_button_change_mode(self, state: int = 1) -> None: + """! Set the button change mode. + + @param state: Change mode state value (1 to enable, 0 to disable). + """ + self.write("BTN_SWITCH_MODE", bytes([state])) + + def get_button_change_mode(self) -> int: + """! Get the button change mode. + + @return: The current button change mode value. + """ + return self.read("BTN_SWITCH_MODE", 1)[0] + + def set_motor_jam_protect_state(self, state: int = 1) -> None: + """! Set the motor jam protection enable/disable. + + @param state: Protection state value (1 to enable, 0 to disable). + """ + self.write("MOTOR_JAM", bytes([state])) + + def get_motor_jam_protect_state(self) -> bool: + """! Get the motor jam protection status. + + @return: True if jam protection is enabled, False otherwise. + """ + return self.read("MOTOR_JAM", 1)[0] != 0 + + def set_motor_id(self, id: int = 0) -> None: + """! Set the motor ID. + + @param id: The ID to assign to the motor. + """ + self.write("MOTOR_ID", bytes([id])) + + def get_motor_id(self) -> int: + """! Get the motor ID. + + @return: The current motor ID. + """ + return self.read("MOTOR_ID", 1)[0] + + def set_485_baudrate(self, bps: int = 0) -> None: + """! Set the 485 baudrate. + + @param bps: Baud rate value. + """ + self.write("BPS_RS485", bytes([bps])) + + def get_485_baudrate(self) -> int: + """! Get the 485 baudrate. + + @return: The current 485 baudrate. + """ + return self.read("BPS_RS485", 1)[0] + + def set_rgb_brightness(self, bright: int = 0) -> None: + """! Set RGB brightness. + + @param bright: Brightness value. + """ + self.write("RGB_BRIGHT", bytes([bright])) + + def get_rgb_brightness(self) -> int: + """! Get RGB brightness. + + @return: The current RGB brightness value. + """ + return self.read("RGB_BRIGHT", 1)[0] + + def set_motor_speed(self, speed: int) -> None: + """! Set the motor speed and max current setting. + + @param speed: The speed value to set. + """ + speed *= 100 + self.write("SPEED_SETTING", struct.pack(" int: + """! Get the motor speed and max current setting. + + @return: The current motor speed. + """ + buf = self.read("SPEED_SETTING", 4) + return int(struct.unpack(" None: + """! Set the motor speed and max current setting. + + @param current: The max current value to set. + """ + current *= 100 + self.write("SPEED_MAX_CURRENT", struct.pack(" int: + """! Get the motor speed and max current setting. + + @return: The current max current setting. + """ + buf = self.read("SPEED_MAX_CURRENT", 4) + return int(struct.unpack(" float: + """! Get the motor speed readback. + + @return: The readback value of the motor speed. + """ + buf = self.read("SPEED_READBACK", 4) + return int(struct.unpack(" None: + """! Set the motor speed PID. + + @param p: Proportional gain. + @param i: Integral gain. + @param d: Derivative gain. + """ + p *= 100000 + i *= 10000000 + d *= 100000 + self.write("SPEED_PID", struct.pack(" tuple: + """! Get the motor speed PID. + + @return: A tuple containing the PID values. + """ + buf = self.read("SPEED_PID", 12) + return tuple(a / b for a, b in zip(struct.unpack(" None: + """! Set the motor position and max current setting. + + @param position: The position value to set. + """ + position *= 100 + self.write("POSITION_SETTING", struct.pack(" int: + """! Get the motor position and max current setting. + + @return: The current motor position. + """ + buf = self.read("POSITION_SETTING", 4) + return int(struct.unpack(" None: + """! Set the motor position and max current setting. + + @param current: The max current value to set. + """ + current *= 100 + self.write("POSITION_MAX_CURRENT", struct.pack(" int: + """! Get the motor position and max current setting. + + @return: The current max current setting. + """ + buf = self.read("POSITION_MAX_CURRENT", 4) + return int(struct.unpack(" float: + """! Get the motor position readback. + + @return: The readback value of the motor position. + """ + buf = self.read("POSITION_READBACK", 4) + return struct.unpack(" tuple: + """! Get the motor position PID. + + @return: A tuple containing the PID values for position. + """ + buf = self.read("POSITION_PID", 12) + return struct.unpack(" None: + """! Set the motor position PID. + + @param p: Proportional gain. + @param i: Integral gain. + @param d: Derivative gain. + """ + p *= 100000 + i *= 10000000 + d *= 100000 + self.write("POSITION_PID", struct.pack(" None: + """! Set the motor max current. + + @param current: The maximum current for the motor, multiplied by 100 before sending. + """ + current *= 100 + self.write("MAX_CURRENT", struct.pack(" int: + """! Get the motor max current. + + @return: The motor max current, divided by 100 after reading. + """ + buf = self.read("MAX_CURRENT", 4) + return int(struct.unpack(" float: + """! Get the motor current readback. + + @return: The motor current readback value, divided by 100 after reading. + """ + buf = self.read("CURRENT_READBACK", 4) + return struct.unpack(" None: + """! Set the system RGB color. + + @param rgb: The RGB color value, where the format is 0xRRGGBB. + """ + r = (rgb >> 16) & 0xFF + g = (rgb >> 8) & 0xFF + b = rgb & 0xFF + self.write("SYSTEM_RGB", bytes([b, g, r])) + + def get_rgb_color(self) -> tuple: + """! Get the system RGB color. + + @return: The RGB color as a tuple (R, G, B). + """ + buf = self.read("SYSTEM_RGB", 3) + return tuple(reversed(struct.unpack("BBB", buf))) + + def set_rgb_mode(self, mode: int) -> None: + """! Set the system RGB mode. + + @param mode: The RGB mode value. + """ + self.write("SYSTEM_RGB_MODE", bytes([mode])) + + def get_rgb_mode(self) -> int: + """! Get the system RGB mode. + + @return: The current RGB mode value. + """ + return self.read("SYSTEM_RGB_MODE", 1)[0] + + def get_vin_voltage(self) -> int: + """! Get the system VIN voltage. + + @return: The system VIN voltage value, multiplied by 10 after reading. + """ + buf = self.read("SYSTEM_VIN", 4) + return struct.unpack(" int: + """! Get the system temperature. + + @return: The current system temperature value. + """ + buf = self.read("SYSTEM_TEMP", 4) + return struct.unpack(" None: + """! Set the system encoder value. + + @param count: The encoder count value. + """ + self.write("SYSTEM_ENCODER", struct.pack(" int: + """! Get the system encoder value. + + @return: The current encoder value. + """ + buf = self.read("SYSTEM_ENCODER", 4) + return struct.unpack(" None: + """! Save the motor data to flash.""" + time.sleep_ms(10) + self.write("SYSTEM_FLASH_WRITE", b"\x01") + time.sleep_ms(200) + + def get_firmware_version(self) -> int: + """! Get the device firmware version. + + @return: The current firmware version. + """ + return self.read("SYSTEM_FIRMWARE", 1)[0] + + def set_i2c_address(self, addr: int) -> None: + """! Set the I2C address. + + @param addr: The new I2C address. Must be between 0x08 and 0x77. + """ + if 0x08 <= addr <= 0x77: + time.sleep_ms(10) + if addr != self._i2c_addr: + self.write("SYSTEM_ADDRESS", struct.pack("B", addr)) + self._i2c_addr = addr + time.sleep_ms(200) + + def get_i2c_address(self) -> int: + """! Get the current I2C address. + + @return: The current I2C address. + """ + return self.read("SYSTEM_ADDRESS", 1)[0] + + +class RollerI2C(RollerBase): + def __init__( + self, i2c: I2C | PAHUBUnit, address: int = _ROLLER485_I2C_ADDR, mode=None + ) -> None: + """! Initialize the RollerI2C object. + + @param i2c: I2C bus instance or PAHUBUnit instance. + @param address: I2C address of the device. Defaults to _ROLLER485_I2C_ADDR. + """ + self._i2c_bus = i2c + self._i2c_addr = address + super().__init__() + + def read(self, register, length) -> bytes: + """! Read data from a specified register on the I2C device. + + @param register: The name of the register to read from. + @param length: The number of bytes to read. + @return: The data read from the device as a bytes object. + """ + return self._i2c_bus.readfrom_mem(self._i2c_addr, _COMMON_REG[register][0], length) + + def write(self, register, bytes) -> None: + """! Write data to a specified register on the I2C device. + + @param register: The name of the register to write to. + @param bytes: The data to write to the register as a bytes object. + """ + self._i2c_bus.writeto_mem(self._i2c_addr, _COMMON_REG[register][0], bytes) + + +class Roller485(RollerBase): + def __init__(self, bus, address=_ROLLER485_RS485_ADDR, mode=None) -> None: + """! Initialize the Roller485 object. + + @param bus: The RS485 bus instance. + @param address: The motor's RS485 address. Defaults to _ROLLER485_RS485_ADDR. + """ + self._rs485_bus = bus + self._rs485_addr = address # motor id == address + super().__init__() + self._obuffer = bytearray(15) # Output buffer for sending commands. + self._ibuffer = bytearray(17) # Input buffer for receiving responses. + self.setting_buf = [0] * 8 # Buffer for storing settings. + self.rgb_buf = [0] * 8 # Buffer for storing RGB data. + + def read(self, register, length): + """! Read data from a specified register via RS485. + + @param register: The name of the register to read from. + @param length: The number of bytes to read. + @return: The data read from the device. + """ + self.send_command(_COMMON_REG[register][3], self._rs485_addr, 0, buf_len=4) + success, output = self.read_response(_COMMON_REG[register][3], self._rs485_addr) + if success: + return output[_COMMON_REG[register][4] : (_COMMON_REG[register][4] + length)] + + def create_frame(self, cmd, motor_id, *datas) -> None: + """! Create a command frame with the given command and motor ID. + + @param cmd: The command byte. + @param motor_id: The ID of the motor. + @param datas: Additional data bytes to include in the frame. + """ + struct.pack_into(">B", self._obuffer, 0, cmd) + struct.pack_into(">B", self._obuffer, 1, motor_id) + for i, data in enumerate(datas): + struct.pack_into(">B", self._obuffer, 2 + i, data) + + def write(self, register, bytes) -> bool: + """! Write data to a specified register via RS485. + + @param register: The name of the register to write to. + @param bytes: The data to write to the register as a bytes object. + @return: The response after writing the data. + """ + if _COMMON_REG[register][5]: + data_buf = [0] * len(bytes) * 2 + if _COMMON_REG[register][5] == 1: + self.setting_buf[ + (_COMMON_REG[register][2] - 2) : (_COMMON_REG[register][2] - 2 + len(bytes)) + ] = [int(b) for b in bytes] + data_buf = self.setting_buf + else: + self.rgb_buf[ + (_COMMON_REG[register][2] - 2) : (_COMMON_REG[register][2] - 2 + len(bytes)) + ] = [int(b) for b in bytes] + data_buf = self.rgb_buf + else: + data_buf = [0] * len(bytes) + + data_buf[(_COMMON_REG[register][2] - 2) : (_COMMON_REG[register][2] - 2 + len(bytes))] = [ + int(b) for b in bytes + ] + self.send_command(_COMMON_REG[register][1], self._rs485_addr, data_buf, buf_len=15) + return self.read_response(_COMMON_REG[register][1], self._rs485_addr)[0] + + def send_command(self, cmd, id, data, buf_len=15) -> None: + """! Send a command via RS485. + + @param cmd: The command byte. + @param id: The motor ID. + @param data: The data to send along with the command. + @param buf_len: The length of the buffer. + """ + cmd_buf = [0] * buf_len + cmd_buf[0] = cmd + cmd_buf[1] = id + if isinstance(data, int): + data = [data] + cmd_buf[2 : (2 + len(data))] = data + cmd_buf[-1] = self._crc8(cmd_buf[:-1]) + self._rs485_bus.write(bytes(cmd_buf)) + + def read_response(self, cmd, id): + """! Read the response from the RS485 bus. + + @param cmd: The command byte. + @param id: The motor ID. + @return: A tuple (success, response). Success is True if the response is valid, and response is the data read. + """ + # 根据 cmd 确定响应包的长度 + cmd += 0x10 # Write cmd+10 = response cmd + if cmd in [ + 0x10, + 0x11, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + ] or cmd in range(0x30, 0x35): + expected_length = 15 + 2 + elif cmd in range(0x50, 0x54): # cmd 0x50~0x53 + expected_length = 18 + 2 + elif cmd in [0x70, 0x72]: # cmd 0x70 和 0x72 + expected_length = 25 + 2 + elif cmd in [0x71, 0x73]: # cmd 0x71 和 0x73 + expected_length = 4 + 2 + else: + print(f"Unsupported cmd: {cmd:#04x}") + return (False, 0) # 返回失败状态 + + time_out = time.ticks_ms() + 500 + resp_buf = bytearray() # 用于存储拼接的响应数据 + while time_out > time.ticks_ms(): + if self._rs485_bus.any(): + new_data = self._rs485_bus.read() + resp_buf.extend(new_data) # 将新读到的数据拼接到 resp_buf + # print(f"Response RAW: {[hex(byte) for byte in resp_buf]}") + if len(resp_buf) < expected_length: # 检查当前已接收到的长度是否满足预期 + # print(f"Partial response received, current length: {len(resp_buf)}") + continue + if len(resp_buf) != expected_length: # 如果数据多于预期,打印并继续处理 + print( + f"Unexpected response length: {len(resp_buf)}, expected: {expected_length}" + ) + resp_buf[:] = b"" # 清除当前数据,重新接收 + continue + if resp_buf[0:2] == b"\xAA\x55": # 检查包头 + if resp_buf[2:4] == bytes([(cmd), id]): + if self._crc8(resp_buf[2:-1]) == resp_buf[-1]: + # print(f"Response: {[hex(byte) for byte in resp_buf]}") + return (True, resp_buf[2:-1]) + time.sleep_ms(100) + return (False, 0) + + def _crc8(self, buffer) -> int: + """! Calculate CRC8 checksum. + + @param buffer: The data buffer to compute the checksum for. + @return: The computed CRC8 value. + """ + crc = 0x00 + for byte in buffer: + crc ^= byte + for _ in range(8): + if crc & 0x01: + crc = (crc >> 1) ^ 0x8C + else: + crc = crc >> 1 + return crc & 0xFF # return the bottom 8 bits + + +class Roller485ToI2CBus(Roller485): + def __init__(self, bus, address=_ROLLER485_RS485_ADDR, mode=None) -> None: + """! Initialize the Roller485ToI2CBus object. + + @param bus: The RS485 bus instance. + @param address: The motor's RS485 address. Defaults to _ROLLER485_RS485_ADDR. + """ + self._rs485_bus = bus + self._rs485_addr = address # Motor ID == address + super().__init__(bus, address=address) + + def readfrom_mem(self, addr: int, mem_addr: int, nbytes: int) -> bytes: # 0x60 + """Read data from the I2C memory register.""" + chunk_size = 16 # 设置一次最多读取16字节 + byte_len = 2 if mem_addr > 0xFF else 1 # 根据寄存器地址长度确定字节数 + result = bytearray() + blocks = (nbytes + chunk_size - 1) // chunk_size # 计算需要的块数 + + for block in range(blocks): + to_read = min(chunk_size, nbytes - block * chunk_size) # 本次读取的字节数 + if byte_len == 1: + data = [addr, 0, mem_addr + block * chunk_size, 0, to_read] + else: + data = [ + addr, + 1, + mem_addr + block * chunk_size, + (mem_addr + block * chunk_size) >> 8, + to_read, + ] + self.send_command(_READ_I2C_SLAVE_REG_ADDR, self._rs485_addr, data, buf_len=8) + success, output = self.read_response(_READ_I2C_SLAVE_REG_ADDR, self._rs485_addr) + if success and output[2]: + result += output[8 : 8 + to_read] + else: + raise Exception( + f"Read I2C Slave Memory Register failed: register {mem_addr:#04x}, nbytes {nbytes}" + ) + return bytes(result) + + def readfrom_mem_into(self, addr: int, mem_addr: int, buf: bytearray) -> None: + """! Read data from the I2C memory register and store it in the provided buffer. + + @param addr: I2C device address. + @param mem_addr: Memory register address. + @param buf: Buffer to store the data. + """ + data = self.readfrom_mem(addr, mem_addr, len(buf)) + buf[: len(data)] = data + + def writeto_mem(self, addr: int, mem_addr: int, buf) -> Literal[True]: # 0x61 + """Write data to the I2C memory register in chunks.""" + # print(f"addr: {addr:#04x}, mem_addr: {mem_addr:#04x}, buf: {buf}") + byte_len = 2 if mem_addr > 0xFF else 1 + total_len = len(buf) + chunk_size = 16 + for i in range(0, total_len, chunk_size): + chunk = buf[i : i + chunk_size] + data_len = len(chunk) + if byte_len == 1: + data = [addr, 0, mem_addr + i, 0, data_len, 0] + else: + data = [addr, 1, mem_addr + i, (mem_addr + i) >> 8, data_len, 0] + data += chunk + self.send_command(_WRITE_I2C_SLAVE_REG_ADDR, self._rs485_addr, data, buf_len=25) + success, output = self.read_response(_WRITE_I2C_SLAVE_REG_ADDR, self._rs485_addr) + if not (success and output[2]): + raise Exception( + f"Write to I2C Memory Register failed: register {mem_addr:#04x}, chunk {chunk}" + ) + return True + + def readfrom(self, addr: int, nbytes: int) -> bytes: + """Read data from the I2C slave.""" + chunk_size = 16 + blocks = (nbytes + chunk_size - 1) // chunk_size # 计算需要多少块 + result = bytearray() + for block in range(blocks): + to_read: int = min(chunk_size, nbytes - block * chunk_size) + self.send_command(_READ_I2C_SLAVE_ADDR, self._rs485_addr, [addr, to_read], buf_len=5) + success, output = self.read_response(_READ_I2C_SLAVE_ADDR, self._rs485_addr) + if success and output[2]: + result += output[8 : 8 + to_read] + else: + raise Exception(f"Read I2C Slave failed: register {addr:#04x}, nbytes {nbytes}") + return bytes(result) + + def readfrom_into(self, addr: int, buf: bytearray) -> None: # 0x62 + """! Read data from the I2C device and store it in the provided buffer. + + @param addr: I2C device address. + @param buf: Buffer to store the data. + """ + data = self.readfrom(addr, len(buf)) + buf[: len(data)] = data + + def writeto(self, addr: int, buf: bytes | bytearray, stop: bool = True) -> Literal[True]: + """Write data to the I2C slave in chunks.""" + total_len = len(buf) + chunk_size = 16 + + for i in range(0, total_len, chunk_size): + chunk = buf[i : i + chunk_size] + data_len = len(chunk) + stop_bit = stop if i + chunk_size >= total_len else False + data = [addr, data_len, stop_bit, 0, 0, 0] + list(chunk) + self.send_command(_WRITE_I2C_SLAVE_ADDR, self._rs485_addr, data, buf_len=25) + success, output = self.read_response(_WRITE_I2C_SLAVE_ADDR, self._rs485_addr) + if not (success and output[2]): + raise Exception(f"Write to I2C Slave failed for chunk {chunk}") + return True + + def _writeto(self, addr: int, buf: bytes | bytearray, stop: bool = True): + data_len = len(buf) + data = [addr, data_len, stop, 0, 0, 0] + list(buf) + self.send_command(_WRITE_I2C_SLAVE_ADDR, self._rs485_addr, data, buf_len=25) + success, output = self.read_response(_WRITE_I2C_SLAVE_ADDR, self._rs485_addr) + if success and output[2]: + return True + + def scan(self) -> list: + """! Scan for I2C devices on the bus. + + @return: A list of addresses of the found I2C devices. + """ + found_devices = [] + for address in range(0x08, 0x77 + 1): + try: + if self._writeto(address, bytes(0x01), stop=False): + found_devices.append(address) + # print(f"Found device at address {address:#04x}") + except OSError: + pass + return found_devices + + +class Roller485Unit: + """! A factory class to create Roller instances based on communication mode.""" + + I2C_MODE = 0 # I2C mode identifier + RS485_MODE = 1 # RS485 mode identifier + RS485_TO_I2C_MODE = 2 # RS485 to I2C mode identifier + + def __new__(cls, *args, **kwargs) -> RollerBase: + """! Create a new instance of Roller based on the specified communication mode. + + @param args: Positional arguments to be passed to the Roller class constructors. + @param kwargs: Keyword arguments, including the 'mode' key to specify the communication mode. + + @return: An instance of RollerBase or one of its subclasses. + """ + if kwargs["mode"] == cls.I2C_MODE: + cls.instance = RollerI2C(args[0], **kwargs) + elif kwargs["mode"] == cls.RS485_MODE: + cls.instance = Roller485(args[0], **kwargs) + elif kwargs["mode"] == cls.RS485_TO_I2C_MODE: + cls.instance = Roller485ToI2CBus(args[0], **kwargs) + else: + raise ValueError("Invalid mode specified") + return cls.instance diff --git a/m5stack/libs/unit/timerpwr.py b/m5stack/libs/unit/timerpwr.py new file mode 100644 index 00000000..8cb3f35e --- /dev/null +++ b/m5stack/libs/unit/timerpwr.py @@ -0,0 +1,598 @@ +# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + + +import struct +import micropython +import time +from .unit_helper import UnitError + + +class TimerPWRUnit: + """ + note: + en: The TimerPWR Unit is a timed power supply unit whose main functions are "charging & discharging + timed switching + screen display + boost output." It features an internal STM32 microcontroller that implements RTC and overall control, allowing users to set automatic power on/off times as needed. It is powered via the Type-C interface and can be connected to an external rechargeable battery via a 1.25-2P interface. The unit includes a built-in battery charging circuit supporting a charging current of 330mA. It also features an integrated DCDC boost circuit that provides a 5V/800mA (1400mA @ 1C battery power) power output to external devices via the Grove port. Additionally, the INA3221 sensor is built-in, allowing real-time monitoring of power input and output current and voltage. The unit is equipped with a 0.66-inch OLED display and two side buttons for user interaction, making it easy to view real-time system status and modify settings. Users can set parameters such as power on/off using the side buttons or via the I2C bus through the Grove interface with I2C commands. This product is suitable for smart homes, industrial automation, and timed control devices. + details: + color: '#0fb1d2' + link: https://docs.m5stack.com/en/unit/Unit-TimerPWR + image: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/products/unit/Unit-TimerPWR/4.webp + category: Unit + example: + - ../../../examples/unit/timerpwr/atoms3_timerpwr_example.py + """ + + """ + constant: register address. + """ + _SLEEP_COMMAND_REG = 0x40 + _CYCLE_REG = 0x41 + + _GROVE_OUTPUT_REG = 0x00 + _OLED_BACKLIGHT_REG = 0x01 + + _WAKE_UP_TRIGGER_REG = 0x20 + _SLEEP_TRIGGER_REG = 0x21 + + _POWER_ON_TIME_REG = 0x30 + _POWER_OFF_TIME_REG = 0x80 + + _BUTTON_STATUS_REG = 0x10 + + _USB_VOLTAGE_REG = 0x60 + _USB_CURRENT_REG = 0x64 + + _GROVE_VOLTAGE_REG = 0x68 + _GROVE_CURRENT_REG = 0x6C + + _BATTERY_VOLTAGE_REG = 0x70 + _BATTERY_CURRENT_REG = 0x74 + + _CHARGING_STATUS_REG = 0x90 + + _SAVE_DATA_TO_FLASH_REG = 0xF0 + + _FW_VERSION_REG = 0xFE + _I2C_ADDRESS_REG = 0xFF + + """ + constant: trigger type. + """ + TRIG_ALL = 0b0000_0011 + TRIG_TIMER = 0b0000_0001 + TRIG_BUTTON = 0b0000_0010 + TRIG_I2C = 0b0000_0100 + TRIG_NONE = 0b0000_0000 + + """ + constant: event type. + """ + EVENT_USB_INSERTED = 0x01 + EVENT_USB_REMOVED = 0x02 + EVENT_BUTTONA_RELEASED = 0x03 + EVENT_BUTTONA_PRESSED = 0x04 + EVENT_BUTTONB_RELEASED = 0x05 + EVENT_BUTTONB_PRESSED = 0x06 + EVENT_NOT_CHARGING = 0x07 + EVENT_CHARGING = 0x08 + + """ + constant: index. + """ + _USB = 0x00 + _BUTTON_A = 0x01 + _BUTTON_B = 0x02 + _CHARGING = 0x03 + + def __init__(self, i2c, address: int | list | tuple = 0x56): + """ + note: + en: Create a TimerPWR object. + label: + en: Init %1 I2C address %2 + params: + i2c: + name: i2c + field: i2c + note: I2C object + address: + name: address + field: dropdown + options: + '0x40': '0x40' + note: I2C address, 0x56 by default + """ + self._i2c = i2c + self._address = address + if self._address not in self._i2c.scan(): + raise UnitError("TimerPWR unit maybe not connect") + usb_status = ( + self.EVENT_USB_INSERTED if self.get_usb_voltage() > 4900 else self.EVENT_USB_REMOVED + ) + self.last_status = [ + usb_status, + self.get_button_status(btn=0), + self.get_button_status(btn=1), + self.is_charging(), + ] # usb status, buttonA status, buttonB status, charging status + self.callbacks = { + self.EVENT_USB_INSERTED: None, + self.EVENT_USB_REMOVED: None, + self.EVENT_BUTTONA_PRESSED: None, + self.EVENT_BUTTONA_RELEASED: None, + self.EVENT_BUTTONB_PRESSED: None, + self.EVENT_BUTTONB_RELEASED: None, + self.EVENT_NOT_CHARGING: None, + self.EVENT_CHARGING: None, + } + + def get_firmware_version(self) -> int: + """ + note: + en: Get firmware version. + label: + en: get %1 firmware version (return int) + return: + note: Firmware version. + + """ + return self._i2c.readfrom_mem(self._address, self._FW_VERSION_REG, 1)[0] + + def get_battery_voltage(self) -> int: + """ + note: + en: Get battery voltage. + label: + en: get %1 battery voltage in millivolt(return int) + return: + note: Battery voltage, in millivolt. + """ + buf = self._i2c.readfrom_mem(self._address, self._BATTERY_VOLTAGE_REG, 4) + return struct.unpack(" int: + """ + note: + en: Get battery current. + label: + en: get %1 battery current in milliamperes(return int) + return: + note: Battery current, in milliamperes. + """ + buf = self._i2c.readfrom_mem(self._address, self._BATTERY_CURRENT_REG, 4) + return struct.unpack(" int: + """ + note: + en: Get USB voltage. + label: + en: get %1 usb voltage in millivolt(return int) + return: + note: USB voltage, in millivolt. + """ + buf = self._i2c.readfrom_mem(self._address, self._USB_VOLTAGE_REG, 4) + return struct.unpack(" int: + """ + note: + en: Get USB current. + label: + en: get %1 usb current in milliamperes(return int) + return: + note: USB current, in milliamperes. + """ + buf = self._i2c.readfrom_mem(self._address, self._USB_CURRENT_REG, 4) + return struct.unpack(" int: + """ + note: + en: Get Grove voltage. + label: + en: get %1 grove voltage in millivolt(return int) + return: + note: Grove voltage, in millivolt. + """ + buf = self._i2c.readfrom_mem(self._address, self._GROVE_VOLTAGE_REG, 4) + return struct.unpack(" int: + """ + note: + en: Get Grove current. + label: + en: get %1 grove current in milliamperes(return int) + return: + note: Grove current, in milliamperes. + """ + buf = self._i2c.readfrom_mem(self._address, self._GROVE_CURRENT_REG, 4) + return struct.unpack(" bool: + """ + note: + en: Check if the battery is charging. + label: + en: '%1 is battery charging (return True or False)' + return: + note: True if charging, False if not. + """ + return bool(self._i2c.readfrom_mem(self._address, self._CHARGING_STATUS_REG, 1)[0]) + + def get_button_status(self, btn=0) -> bool: + """ + note: + en: Get button status. + label: + en: get %1 button %2 status (return True or False) + params: + btn: + name: btn + field: dropdown + options: + A: '0' + B: '1' + return: + note: True if pressed, False if not. + """ + status = self._i2c.readfrom_mem(self._address, self._BUTTON_STATUS_REG + btn, 1)[0] + return True if status == 0 else False + + def save_data_to_flash(self) -> None: + """ + note: + en: Save data to flash. + label: + en: Save %1 data to flash + """ + self._i2c.writeto_mem(self._address, self._SAVE_DATA_TO_FLASH_REG, b"\x01") + time.sleep(0.1) + + def get_grove_output_status(self) -> bool: + """ + note: + en: Get Grove output status + label: + en: get %1 Grove output status (return True or False) + return: + note: True if enabled, False if disabled. + """ + return bool(self._i2c.readfrom_mem(self._address, self._GROVE_OUTPUT_REG, 1)[0]) + + def set_grove_output_status(self, enable: bool) -> None: + """ + note: + en: Set Grove output status. + label: + en: Set %1 Grove output %2 + params: + enable: + name: enable + field: dropdown + options: + Enable: 'True' + Disable: 'False' + note: Enable or disable Grove output. + """ + self._i2c.writeto_mem( + self._address, self._GROVE_OUTPUT_REG, b"\x01" if enable else b"\x00" + ) + + def get_oled_backlight_status(self) -> bool: + """ + note: + en: Get OLED backlight status. + label: + en: get %1 Grove oled backlight status (return True or False) + return: + note: True if enabled, False if disabled. + """ + return bool(self._i2c.readfrom_mem(self._address, self._OLED_BACKLIGHT_REG, 1)[0]) + + def set_oled_backlight_status(self, enable: bool) -> None: + """ + note: + en: Set OLED backlight status. + label: + en: Set %1 oled backlight %2 + params: + enable: + name: enable + field: dropdown + options: + Enable: 'True' + Disable: 'False' + note: Enable or disable OLED backlight. + """ + self._i2c.writeto_mem( + self._address, self._OLED_BACKLIGHT_REG, b"\x01" if enable else b"\x00" + ) + + def sleep_once( + self, + whours: int = 0, + wmintues: int = 0, + wseconds: int = 5, + shours: int = 0, + smintues: int = 0, + sseconds: int = 5, + ) -> None: + """ + note: + en: Set sleep once after hours, mintues, seconds and wake up in hours, mintues, seconds. + label: + en: set %1 sleep once after hours (0 ~ 255) %2 mintues (0 ~ 59) %3 seconds (0 + ~ 59) %4 and wake up in hours (0 ~ 255) %5 mintues (0 ~ 59) %6 seconds (0 + ~ 59) %7 + params: + whours: + name: whours + type: int + default: '0' + field: number + note: Hours to wait before sleep. + wmintues: + name: wmintues + type: int + default: '0' + field: number + note: Mintues to wait before sleep. + wseconds: + name: wseconds + type: int + default: '5' + field: number + note: Seconds to wait before sleep. + shours: + name: shours + type: int + default: '0' + field: number + note: Hours to wait before wake up. + smintues: + name: param_6f3 + type: int + default: '0' + field: number + note: Mintues to wait before wake up. + sseconds: + name: sseconds + type: int + default: '5' + field: number + note: Seconds to wait before wake up. + """ + self.set_power_on_time(whours, wmintues, wseconds) + self.set_power_off_time(shours, smintues, sseconds) + self.save_data_to_flash() + self._i2c.writeto_mem(self._address, self._SLEEP_COMMAND_REG, b"\x01") + + def set_power_on_time(self, hours: int = 0, mintues: int = 0, seconds: int = 5) -> None: + """ + note: + en: Set power on time. + label: + en: set %1 power on time hours (0 ~ 255) %2 mintues (0 ~ 59) %3 seconds (0 ~ 59) %4 + params: + hours: + name: hours + type: int + default: '0' + field: number + note: Hours to power on. + mintues: + name: mintues + type: int + default: '0' + field: number + note: Mintues to power on. + seconds: + name: seconds + type: int + default: '5' + field: number + note: Seconds to power on. + """ + buf = struct.pack(">BBB", hours, mintues, seconds) + self._i2c.writeto_mem(self._address, self._POWER_ON_TIME_REG, buf) + + def set_power_off_time(self, hours: int = 0, mintues: int = 0, seconds: int = 5) -> None: + """ + note: + en: Set power off time. + label: + en: set %1 power off time hours (0 ~ 255) %2 mintues (0 ~ 59) %3 seconds (0 ~ 59) %4 + params: + hours: + name: hours + type: int + default: '0' + field: number + note: Hours to power off. + mintues: + name: mintues + type: int + default: '0' + field: number + note: Mintues to power off. + seconds: + name: seconds + type: int + default: '5' + field: number + note: Seconds to power off. + """ + buf = struct.pack(">BBB", hours, mintues, seconds) + self._i2c.writeto_mem(self._address, self._POWER_OFF_TIME_REG, buf) + + def sleep_cycle( + self, + whours: int = 0, + wmintues: int = 0, + wseconds: int = 5, + shours: int = 0, + smintues: int = 0, + sseconds: int = 5, + ) -> None: + """ + note: + en: Set sleep cycle after hours, mintues, seconds and wake up in hours, mintues, seconds. + label: + en: set %1 sleep cycle after hours (0 ~ 255) %2 mintues (0 ~ 59) %3 seconds (0 + ~ 59) %4 and wake up in hours (0 ~ 255) %5 mintues (0 ~ 59) %6 seconds (0 + ~ 59) %7 + params: + whours: + name: whours + type: int + default: '0' + field: number + note: Hours to wait before sleep. + wmintues: + name: wmintues + type: int + default: '0' + field: number + note: Mintues to wait before sleep. + wseconds: + name: wseconds + type: int + default: '5' + field: number + note: Seconds to wait before sleep. + shours: + name: shours + type: int + default: '0' + field: number + note: Hours to wait before wake up. + smintues: + name: param_6f3 + type: int + default: '0' + field: number + note: Mintues to wait before wake up. + sseconds: + name: sseconds + type: int + default: '5' + field: number + note: Seconds to wait before wake up. + """ + self.set_power_on_time(whours, wmintues, wseconds) + self.set_power_off_time(shours, smintues, sseconds) + self.save_data_to_flash() + self._i2c.writeto_mem(self._address, self._CYCLE_REG, b"\x01") + + def set_cycle_sleep(self, enable: bool) -> None: + """ + note: + en: Set cycle sleep. + label: + en: set %1 cycle sleep %2 + params: + enable: + name: enable + field: dropdown + options: + Enable: 'True' + Disable: 'False' + note: Enable or disable cycle sleep. + """ + self._i2c.writeto_mem(self._address, self._CYCLE_REG, b"\x01" if enable else b"\x00") + + def set_wakeup_trigger(self, trigger) -> None: + """ + note: + en: Set wake-up trigger. + label: + en: set %1 wake-up trigger %2 + params: + trigger: + name: trigger + field: dropdown + options: + ALL: timerpwrunit_0.TRIG_ALL + TIMER: timerpwrunit_0.TRIG_TIMER + BUTTON: timerpwrunit_0.TRIG_BUTTON + NONE: timerpwrunit_0.TRIG_NONE + note: Set wake-up trigger. + """ + self._i2c.writeto_mem(self._address, self._WAKE_UP_TRIGGER_REG, bytes([trigger])) + + def set_sleep_trigger(self, trigger) -> None: + """ + note: Set sleep trigger. + label: + en: set %1 sleep trigger %2 + params: + trigger: + name: trigger + field: dropdown + options: + ALL: timerpwrunit_0.TRIG_ALL + TIMER: timerpwrunit_0.TRIG_TIMER + BUTTON: timerpwrunit_0.TRIG_BUTTON + I2C: timerpwrunit_0.TRIG_I2C + NONE: timerpwrunit_0.TRIG_NONE + note: Set sleep trigger. + """ + self._i2c.writeto_mem(self._address, self._SLEEP_TRIGGER_REG, bytes([trigger])) + + def set_callback(self, event, callback) -> None: + """ + note: + en: Set callback function. + params: + event: + name: event + field: dropdown + options: + USB inserted: timerpwrunit_0.EVENT_USB_INSERTED + USB removed: timerpwrunit_0.EVENT_USB_REMOVED + Button A pressed: timerpwrunit_0.EVENT_BUTTONA_PRESSED + Button A released: timerpwrunit_0.EVENT_BUTTONA_RELEASED + Button B pressed: timerpwrunit_0.EVENT_BUTTONB_PRESSED + Button B released: timerpwrunit_0.EVENT_BUTTONB_RELEASED + Not charging: timerpwrunit_0.EVENT_NOT_CHARGING + Charging: timerpwrunit_0.EVENT_CHARGING + callback: + name: callback + type: function + """ + self.callbacks[event] = callback + + def tick(self): + """ + note: + en: Update status in loop. + label: + en: '%1 update in loop' + """ + usb_status = ( + self.EVENT_USB_INSERTED if self.get_usb_voltage() > 4900 else self.EVENT_USB_REMOVED + ) + + if usb_status != self.last_status[self._USB] and self.callbacks[usb_status]: + self.last_status[self._USB] = usb_status + micropython.schedule(self.callbacks[usb_status], (self, usb_status)) + + charging_status = self.is_charging() + if charging_status != self.last_status[self._CHARGING]: + print( + "charging status=%d, last staus=%d" + % (charging_status, self.last_status[self._CHARGING]) + ) + callback = self.callbacks[self.EVENT_NOT_CHARGING + int(charging_status)] + self.last_status[self._CHARGING] = charging_status + callback and micropython.schedule(callback, (self, charging_status)) + + for btn in range(2): + status = self.get_button_status(btn=btn) + callback = self.callbacks[self.EVENT_BUTTONA_RELEASED + btn * 2 + int(status)] + if status != self.last_status[self._BUTTON_A + btn] and callback: + # print("btn%d status=%d, last staus=%d" % (btn, status, self.last_status[btn])) + self.last_status[self._BUTTON_A + btn] = status + micropython.schedule(callback, (self, status)) diff --git a/m5stack/libs/unit/uwb.py b/m5stack/libs/unit/uwb.py index 4c32343c..ba0bd5d5 100644 --- a/m5stack/libs/unit/uwb.py +++ b/m5stack/libs/unit/uwb.py @@ -2,90 +2,273 @@ # # SPDX-License-Identifier: MIT -from machine import UART, Pin -from .unit_helper import UnitError import time import sys +import re +import machine +import micropython +from .unit_helper import UnitError if sys.platform != "esp32": from typing import Literal -UWB_RESULT_HEADER = "an" -UWB_GET_TIMEOUT = 12 +class UWBUnit: + """ + note: + en: | + UWB is a Unit which integrates the UWB(Ultra Wide Band) communication protocol which uses nanosecond pulses to locate objects and define position and orientation. The design uses the Ai-ThinkerBU01 Transceiver module which is based on Decawave's DW1000 design. The internal STM32 chip with its integrated ranging algorithm,is capable of 10cm positioning accuracy and also supports AT command control. Applications include: Indoor wireless tracking/range finding of assets,which works by triangulating the position of the base station/s and tag (the base station resolves the position information and outputs it to the tag). + The firmware currently carried by this Unit only supports the transmission of ranging information, and does not currently support the transmission of custom information. When in use, it supports the configuration of 4 base station devices (using different IDs), and only a single tag device is allowed to operate at the same time. + details: + color: '#0fb1d2' + link: https://docs.m5stack.com/en/unit/uwb + image: https://static-cdn.m5stack.com/resource/docs/products/unit/uwb/uwb_01.webp + category: unit + example: + - ../../../examples/unit/uwb/core2_uwb_anchor_example.py + - ../../../examples/unit/uwb/stickc_plus2_uwb_tag_example.py + """ + """ + constant: device role + """ + UNKNOWN = -1 + ANCHOR = 1 + TAG = 0 + + """ + constant: device status + """ + OFFLINE = 0 + ONLINE = 1 + + _mode_map = { + "ANCHOR": ANCHOR, + "TAG": TAG, + } -class UWBUnit: def __init__( - self, id: Literal[0, 1, 2] = 1, port: list | tuple = None, anchor_id=None, debug=False + self, + id: Literal[0, 1, 2], + port: list | tuple = None, + device_mode=TAG, + device_id=None, + verbose: bool = False, ): - self._debug = debug - Pin(port[0], Pin.IN, Pin.PULL_UP) - self._uart = UART(id, tx=port[1], rx=port[0]) - self._uart.init(115200, bits=8, parity=None, stop=1) - self.tx = port[1] - self.rx = port[0] - self.get_distance_measure = [0.0, 0.0, 0.0, 0.0] - self.continuous_op = False - self.device_id = "" - self.reset() - time.sleep(0.2) - if self.check_device is False: + """ + note: + en: Create a UWB unit object. + label: + en: init %1 UART %5 as %2 with ID %3 verbose %4 + params: + id: + name: id + type: int + default: '2' + field: number + note: UART ID. + device_mode: + name: device_mode + field: dropdown + options: + Anchor: UWBUnit.ANCHOR + Tag: UWBUnit.TAG + note: device mode. + device_id: + name: device_id + type: int + default: '0' + field: number + note: device ID. + verbose: + name: verbose + type: bool + default: 'False' + field: switch + note: verbose output. + """ + machine.Pin(port[0], machine.Pin.IN, machine.Pin.PULL_UP) + self._uart = machine.UART(id, 115200, tx=port[1], rx=port[0]) + self._device_mode = device_mode + self._device_id = device_id + self._verbose = verbose + + if self.isconnected() is False: raise UnitError("UWB unit maybe not connect") - self.continuous_output_value(0) - if anchor_id is None: - self.set_mode() - self.set_range_interval(5) - self.continuous_output_value(1) - else: - self.set_mode(anchor_id) - - def uart_port_id(self, id_num): - self._uart = UART(id_num, tx=self.tx, rx=self.rx) - self._uart.init(115200, bits=8, parity=None, stop=1) - - @property - def check_device(self): + + self._interval = 5 + self.set_device_mode(self._device_mode, self._device_id) + + self._anchor_status = [False, False, False, False] + self._last_anchor_status = [False, False, False, False] + self._distances = [0.0, 0.0, 0.0, 0.0] + self._callbacks = [None, None, None, None, None, None, None, None] + self._anchors = [] + self._last_time = time.time() + + def get_distance(self, index: int) -> float: + """ + note: + en: Get the distance to the anchor ID (0 ~ 3). + label: + en: get %1 distance to anchor ID (0 ~ 3) %2 (meters, return float) + params: + index: + name: index + type: int + default: '0' + field: number + note: anchor ID (0 ~ 3). + return: + note: distance in meters. + """ + return self._distances[index] + + def get_device_id(self) -> int: + """ + note: + en: Get the device ID. + label: + en: get %1 device ID(return int) + return: + note: device ID. + """ + return self._device_id + + def get_device_mode(self) -> int: + """ + note: + en: Get the device mode. + label: + en: get %1 device mode(return int) + return: + note: device mode. + """ + return self._device_mode + + def set_device_mode(self, mode: int, id: int) -> None: + """ + note: + en: Set the device mode and ID. + label: + en: set %1 as %2 with ID %3 + params: + mode: + name: mode + field: dropdown + options: + Anchor: UWBUnit.ANCHOR + Tag: UWBUnit.TAG + note: device mode. + id: + name: id + type: int + default: '0' + field: number + note: device ID. + """ + cmd = f"AT+anchor_tag={mode},{id}\r\n" + self._at_cmd_send(cmd, keyword="OK") + time.sleep(0.1) + self.reset() + self.set_measurement_interval(self._interval) + if mode == self.TAG: + self.set_measurement(True) + self._device_mode = mode + self._device_id = id + + def isconnected(self): + """ + note: + en: Check if the UWB unit is connected. + label: + en: check %1 is connected (return True or False) + return: + note: True if connected, False otherwise. + """ cmd = "AT\r\n" - resp = self.at_cmd_send(cmd, keyword="OK") - if len(resp): - if self.remove_lfcr(resp)[0] == "OK": + resp = self._at_cmd_send(cmd, keyword="OK") + for line in resp: + if line.find("OK") != -1: return True - else: - return False + return False def get_version(self): + """ + note: + en: Get the UWB unit firmware version. + label: + en: get %1 firmware version(return string) + return: + note: firmware version. + """ cmd = "AT+version?\r\n" - response = self.at_cmd_send(cmd, keyword="OK") + response = self._at_cmd_send(cmd, keyword="OK") if len(response) > 2: if "OK" in response[2]: return response[0] - def reset(self): - cmd = "AT+RST\r\n" - resp = self.at_cmd_send(cmd, keyword="OK") - resp = ",".join(self.remove_lfcr(resp)) - if resp.find("device:") != -1: - self.device_id = resp[resp.find("device:") + 7 :] + def reset(self) -> None: + """ + note: + en: Reset the UWB unit. + label: + en: reset %1 + """ + resp = self._at_cmd_send("AT+RST\r\n", keyword="OK") + for line in resp: + if line.find("device:") != -1: + text = self._extract_text(line, "device:", " ") + if text: + self._device_mode = self._mode_map.get(text, self.UNKNOWN) + text = self._extract_text(line, "ID:", "\r\n") + if text: + self._device_id = int(text) - def set_range_interval(self, interval): - cmd = "AT+interval=" + str(interval) + "\r\n" - self.at_cmd_send(cmd, keyword="OK") + @staticmethod + def _extract_text(text, start_str, end_str): + pattern = f"{start_str}(.*?){end_str}" + match = re.search(pattern, text) + return match.group(1) if match else None - def set_mode(self, id=None): - cmd = "AT+anchor_tag=1," + str(id) + "\r\n" if (id is not None) else "AT+anchor_tag=0\r\n" - self.at_cmd_send(cmd, keyword="OK") - time.sleep(0.1) - self.reset() + def set_measurement_interval(self, interval: int) -> None: + """ + note: + en: Set the measurement interval. + label: + en: set %1 range interval %2 (5 ~ 50) times before value output + params: + interval: + name: interval + type: int + default: '5' + field: number + note: measurement interval. + """ + cmd = f"AT+interval={interval}\r\n" + self._at_cmd_send(cmd, keyword="OK") + self._interval = interval - def continuous_output_value(self, start=1): - cmd = "AT+switchdis=" + str(start) + "\r\n" - self.at_cmd_send(cmd, keyword="OK") - self.continuous_op = bool(start) + def set_measurement(self, enable: bool): + """ + note: + en: Set the measurement output. + label: + en: set %1 measurement output %2 + params: + enable: + name: enable + type: bool + default: 'False' + field: switch + note: enable or disable measurement output. + """ + cmd = f"AT+switchdis={int(enable)}\r\n" + self._at_cmd_send(cmd, keyword="OK") + self.continuous_op = enable - def at_cmd_send(self, cmd, keyword=None, timeout=2): - if self._debug: - print("AT command:" + cmd[:-2]) + def _at_cmd_send(self, cmd, keyword=None, timeout=2): + self._verbose and print("AT command:" + cmd[:-2]) self._uart.read() time.sleep(0.1) self._uart.write(cmd) @@ -102,42 +285,87 @@ def at_cmd_send(self, cmd, keyword=None, timeout=2): elif line is None or line == "": continue if keyword is not None and keyword in line: - if self._debug: - print("Got KEYWORD") + self._verbose and print("Got KEYWORD") find_keyword = True if find_keyword is True and self._uart.any() == 0: break - if self._debug: - print(msgs) + self._verbose and print(msgs) return msgs - def remove_lfcr(self, msgs): - # remove "\r\n" - respon = [] - for i in msgs: - if i != "\r\n": - if len(i) > 2 and i[-2:] == "\r\n": - i = i[:-2] - respon.append(i) - if self._debug: - print(respon) - return respon - - def update_new_value_loop(self): - rx_times = 0 - while True: - if self._uart.any() < 40: - break - while (rx_times < 4) and self.continuous_op and ("TAG ID:" in self.device_id): - if self._uart.any(): - line = self._uart.readline() - if line is not None: - if UWB_RESULT_HEADER in line: - line = line.decode() - separate = line.find(":") - if rx_times == int(line[separate - 1]): - self.get_distance_measure[int(line[separate - 1])] = float( - line[separate + 1 : -3] - ) - rx_times += 1 + def set_callback(self, anchor: int, event: int, callback): + """ + note: + en: Set the callback function for the anchor status. + params: + anchor: + name: anchor + type: int + default: '0' + field: number + note: anchor ID (0 ~ 3). + event: + name: event + field: dropdown + options: + ONLINE: UWBUnit.ONLINE + OFFLINE: UWBUnit.OFFLINE + note: anchor status. + callback: + name: callback + type: function + note: callback function. + """ + self._callbacks[anchor * 2 + event] = callback + + def update(self): + """ + note: + en: Update the distances and anchor status. + label: + en: '%1 update in loop' + """ + if self.continuous_op and self._device_mode == self.TAG and self._uart.any(): + line = self._uart.readline() + # parse anchor id and distance + text = self._extract_text(line, "an", ":") + index = int(text) if text else None + text = self._extract_text(line, ":", "m\r\n") + distance = float(text) if text else None + if index is None or distance is None: + return + + self._last_time = time.time() + + if self._anchors.count(index) == 0: + self._anchors.append(index) + else: + self._anchor_status = [False, False, False, False] + for i in self._anchors: + self._anchor_status[i] = True + self._anchors.clear() + + # handler anchor status + for i in range(4): + if self._last_anchor_status[i] != self._anchor_status[i]: + self._last_anchor_status[i] = self._anchor_status[i] + callback = self._callbacks[i * 2 + int(self._anchor_status[i])] + callback and micropython.schedule(callback, (self, i)) + + # handler distance + self._distances[index] = distance + # FIXME: Create a callback for each measurement? + # callback = self._callbacks[index * 2] + # callback and micropython.schedule(callback, (self, index)) + + # timeout seconds + if time.time() - self._last_time > self._interval: + self._anchors.clear() + # handler anchor status + for i in range(4): + self._anchor_status[i] = False + if self._last_anchor_status[i] != self._anchor_status[i]: + self._last_anchor_status[i] = self._anchor_status[i] + callback = self._callbacks[i * 2 + int(self._anchor_status[i])] + callback and micropython.schedule(callback, (self, i)) + self._last_time = time.time() diff --git a/m5stack/main.c b/m5stack/main.c index a6e50341..16f0effe 100644 --- a/m5stack/main.c +++ b/m5stack/main.c @@ -236,9 +236,9 @@ void mp_task(void *pvParameter) { void boardctrl_startup(void) { // cores3 / core2 / tough - #if BOARD_ID == 10 || BOARD_ID == 2 || BOARD_ID == 8 + // #if BOARD_ID == 10 || BOARD_ID == 2 || BOARD_ID == 8 board_init(); - #endif + // #endif esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { diff --git a/m5stack/patches/1001-Fix-I2C-timeout.patch b/m5stack/patches/1001-Fix-I2C-timeout.patch new file mode 100644 index 00000000..945751bd --- /dev/null +++ b/m5stack/patches/1001-Fix-I2C-timeout.patch @@ -0,0 +1,42 @@ +diff --git a/components/driver/i2c.c b/components/driver/i2c.c +index 85c6f65..4b15b4e 100644 +--- a/components/driver/i2c.c ++++ b/components/driver/i2c.c +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -1494,7 +1494,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, + // Sometimes when the FSM get stuck, the ACK_ERR interrupt will occur endlessly until we reset the FSM and clear bus. + esp_err_t ret = ESP_FAIL; + i2c_obj_t *p_i2c = p_i2c_obj[i2c_num]; +- TickType_t ticks_start = xTaskGetTickCount(); ++ const TickType_t ticks_start = xTaskGetTickCount(); + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; +@@ -1534,13 +1534,15 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, + i2c_cmd_evt_t evt; + while (1) { + TickType_t wait_time = xTaskGetTickCount(); +- if (wait_time - ticks_start > ticks_to_wait) { // out of time +- wait_time = I2C_CMD_ALIVE_INTERVAL_TICK; ++ const TickType_t elapsed = wait_time - ticks_start; ++ if (elapsed >= ticks_to_wait) { // out of time ++ /* Before triggering a timeout, empty the queue by giving a wait_time of 0: ++ * - if the queue is empty, `pdFALSE` will be returned and the loop will be exited ++ * - if the queue is not empty, we will pop an element and come back here again ++ */ ++ wait_time = 0; + } else { +- wait_time = ticks_to_wait - (wait_time - ticks_start); +- if (wait_time < I2C_CMD_ALIVE_INTERVAL_TICK) { +- wait_time = I2C_CMD_ALIVE_INTERVAL_TICK; +- } ++ wait_time = MIN(ticks_to_wait - elapsed, I2C_CMD_ALIVE_INTERVAL_TICK); + } + // In master mode, since we don't have an interrupt to detective bus error or FSM state, what we do here is to make + // sure the interrupt mechanism for master mode is still working.