数码之家

 找回密码
 立即注册
搜索
查看: 2531|回复: 3

[Arduino] [esp8266_micropython] EC11旋转编码器

[复制链接]
发表于 2023-8-28 22:02:37 | 显示全部楼层 |阅读模式
本帖最后由 mao_jin_dao 于 2023-8-29 21:36 编辑

项目目标
esp8266设备A读取EC11旋转编码器数值,通过mqtt发送数据;
esp8266设备B通过mqtt接收数据,并绘制在ssd1306 oled屏幕上。


编写代码
设备A代码
main.py
  1. import time
  2. import json
  3. from rotary_irq_esp import RotaryIRQ
  4. from umqtt.simple import MQTTClient

  5. device_id = "esp0007"
  6. wifi_ssid='FAST_20CC'
  7. wifi_password='409409409'
  8. mqtt_serverip='192.168.1.113'
  9. mqtt_serverport=1883
  10. mqtt_clientid=device_id
  11. mqtt_publishtopic=b'espiot'

  12. message_template = {}
  13. message_template['source_device'] = mqtt_clientid
  14. message_template['target_device'] = 'server'
  15. message_template['msg_type'] = 'ReportRotateEncoder'

  16. def connectionWifi(ssid, password):
  17.   wlan = network.WLAN(network.STA_IF)
  18.   wlan.active(True)
  19.   wlan.connect(ssid, password)
  20.   while True:
  21.     if not wlan.isconnected():
  22.             print ("connecting...")
  23.     else:
  24.       print('connected to network')
  25.       break
  26.     time.sleep(1)

  27. if __name__== '__main__':
  28.   connectionWifi(wifi_ssid,wifi_password)
  29.   
  30.   client = MQTTClient(mqtt_clientid,mqtt_serverip,mqtt_serverport)
  31.   client.connect()
  32.   
  33.   rotate = RotaryIRQ(
  34.     pin_num_clk=4,
  35.     pin_num_dt=5,
  36.     reverse=True,
  37.     incr=1,
  38.     range_mode=RotaryIRQ.RANGE_UNBOUNDED,
  39.     pull_up=True,
  40.     half_step=False,
  41.   )
  42.   
  43.   val_old = rotate.value()
  44.   while True:
  45.     val_new = rotate.value()
  46.     if val_old != val_new:
  47.       val_old = val_new
  48.       print("step =", val_new)
  49.       message_template['step'] = str(val_new)
  50.       client.publish(mqtt_publishtopic,json.dumps(message_template))
  51.       time.sleep_ms(50)
复制代码



rotary_irq_esp.py
  1. # MIT License (MIT)
  2. # Copyright (c) 2020 Mike Teachman
  3. # https://opensource.org/licenses/MIT

  4. # Platform-specific MicroPython code for the rotary encoder module
  5. # ESP8266/ESP32 implementation

  6. # Documentation:
  7. #   https://github.com/MikeTeachman/micropython-rotary

  8. from machine import Pin
  9. from rotary import Rotary
  10. from sys import platform

  11. _esp8266_deny_pins = [16]


  12. class RotaryIRQ(Rotary):

  13.     def __init__(self, pin_num_clk, pin_num_dt, min_val=0, max_val=10, incr=1,
  14.                  reverse=False, range_mode=Rotary.RANGE_UNBOUNDED, pull_up=False, half_step=False, invert=False):

  15.         if platform == 'esp8266':
  16.             if pin_num_clk in _esp8266_deny_pins:
  17.                 raise ValueError(
  18.                     '%s: Pin %d not allowed. Not Available for Interrupt: %s' %
  19.                     (platform, pin_num_clk, _esp8266_deny_pins))
  20.             if pin_num_dt in _esp8266_deny_pins:
  21.                 raise ValueError(
  22.                     '%s: Pin %d not allowed. Not Available for Interrupt: %s' %
  23.                     (platform, pin_num_dt, _esp8266_deny_pins))

  24.         super().__init__(min_val, max_val, incr, reverse, range_mode, half_step, invert)

  25.         if pull_up == True:
  26.             self._pin_clk = Pin(pin_num_clk, Pin.IN, Pin.PULL_UP)
  27.             self._pin_dt = Pin(pin_num_dt, Pin.IN, Pin.PULL_UP)
  28.         else:
  29.             self._pin_clk = Pin(pin_num_clk, Pin.IN)
  30.             self._pin_dt = Pin(pin_num_dt, Pin.IN)

  31.         self._enable_clk_irq(self._process_rotary_pins)
  32.         self._enable_dt_irq(self._process_rotary_pins)

  33.     def _enable_clk_irq(self, callback=None):
  34.         self._pin_clk.irq(
  35.             trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING,
  36.             handler=callback)

  37.     def _enable_dt_irq(self, callback=None):
  38.         self._pin_dt.irq(
  39.             trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING,
  40.             handler=callback)

  41.     def _disable_clk_irq(self):
  42.         self._pin_clk.irq(handler=None)

  43.     def _disable_dt_irq(self):
  44.         self._pin_dt.irq(handler=None)

  45.     def _hal_get_clk_value(self):
  46.         return self._pin_clk.value()

  47.     def _hal_get_dt_value(self):
  48.         return self._pin_dt.value()

  49.     def _hal_enable_irq(self):
  50.         self._enable_clk_irq(self._process_rotary_pins)
  51.         self._enable_dt_irq(self._process_rotary_pins)

  52.     def _hal_disable_irq(self):
  53.         self._disable_clk_irq()
  54.         self._disable_dt_irq()

  55.     def _hal_close(self):
  56.         self._hal_disable_irq()
复制代码


rotary.py
  1. # MIT License (MIT)
  2. # Copyright (c) 2022 Mike Teachman
  3. # https://opensource.org/licenses/MIT

  4. # Platform-independent MicroPython code for the rotary encoder module

  5. # Documentation:
  6. #   https://github.com/MikeTeachman/micropython-rotary

  7. import micropython

  8. _DIR_CW = const(0x10)  # Clockwise step
  9. _DIR_CCW = const(0x20)  # Counter-clockwise step

  10. # Rotary Encoder States
  11. _R_START = const(0x0)
  12. _R_CW_1 = const(0x1)
  13. _R_CW_2 = const(0x2)
  14. _R_CW_3 = const(0x3)
  15. _R_CCW_1 = const(0x4)
  16. _R_CCW_2 = const(0x5)
  17. _R_CCW_3 = const(0x6)
  18. _R_ILLEGAL = const(0x7)

  19. _transition_table = [

  20.     # |------------- NEXT STATE -------------|            |CURRENT STATE|
  21.     # CLK/DT    CLK/DT     CLK/DT    CLK/DT
  22.     #   00        01         10        11
  23.     [_R_START, _R_CCW_1, _R_CW_1,  _R_START],             # _R_START
  24.     [_R_CW_2,  _R_START, _R_CW_1,  _R_START],             # _R_CW_1
  25.     [_R_CW_2,  _R_CW_3,  _R_CW_1,  _R_START],             # _R_CW_2
  26.     [_R_CW_2,  _R_CW_3,  _R_START, _R_START | _DIR_CW],   # _R_CW_3
  27.     [_R_CCW_2, _R_CCW_1, _R_START, _R_START],             # _R_CCW_1
  28.     [_R_CCW_2, _R_CCW_1, _R_CCW_3, _R_START],             # _R_CCW_2
  29.     [_R_CCW_2, _R_START, _R_CCW_3, _R_START | _DIR_CCW],  # _R_CCW_3
  30.     [_R_START, _R_START, _R_START, _R_START]]             # _R_ILLEGAL

  31. _transition_table_half_step = [
  32.     [_R_CW_3,            _R_CW_2,  _R_CW_1,  _R_START],
  33.     [_R_CW_3 | _DIR_CCW, _R_START, _R_CW_1,  _R_START],
  34.     [_R_CW_3 | _DIR_CW,  _R_CW_2,  _R_START, _R_START],
  35.     [_R_CW_3,            _R_CCW_2, _R_CCW_1, _R_START],
  36.     [_R_CW_3,            _R_CW_2,  _R_CCW_1, _R_START | _DIR_CW],
  37.     [_R_CW_3,            _R_CCW_2, _R_CW_3,  _R_START | _DIR_CCW],
  38.     [_R_START,           _R_START, _R_START, _R_START],
  39.     [_R_START,           _R_START, _R_START, _R_START]]

  40. _STATE_MASK = const(0x07)
  41. _DIR_MASK = const(0x30)


  42. def _wrap(value, incr, lower_bound, upper_bound):
  43.     range = upper_bound - lower_bound + 1
  44.     value = value + incr

  45.     if value < lower_bound:
  46.         value += range * ((lower_bound - value) // range + 1)

  47.     return lower_bound + (value - lower_bound) % range


  48. def _bound(value, incr, lower_bound, upper_bound):
  49.     return min(upper_bound, max(lower_bound, value + incr))


  50. def _trigger(rotary_instance):
  51.     for listener in rotary_instance._listener:
  52.         listener()


  53. class Rotary(object):

  54.     RANGE_UNBOUNDED = const(1)
  55.     RANGE_WRAP = const(2)
  56.     RANGE_BOUNDED = const(3)

  57.     def __init__(self, min_val, max_val, incr, reverse, range_mode, half_step, invert):
  58.         self._min_val = min_val
  59.         self._max_val = max_val
  60.         self._incr = incr
  61.         self._reverse = -1 if reverse else 1
  62.         self._range_mode = range_mode
  63.         self._value = min_val
  64.         self._state = _R_START
  65.         self._half_step = half_step
  66.         self._invert = invert
  67.         self._listener = []

  68.     def set(self, value=None, min_val=None, incr=None,
  69.             max_val=None, reverse=None, range_mode=None):
  70.         # disable DT and CLK pin interrupts
  71.         self._hal_disable_irq()

  72.         if value is not None:
  73.             self._value = value
  74.         if min_val is not None:
  75.             self._min_val = min_val
  76.         if max_val is not None:
  77.             self._max_val = max_val
  78.         if incr is not None:
  79.             self._incr = incr
  80.         if reverse is not None:
  81.             self._reverse = -1 if reverse else 1
  82.         if range_mode is not None:
  83.             self._range_mode = range_mode
  84.         self._state = _R_START

  85.         # enable DT and CLK pin interrupts
  86.         self._hal_enable_irq()

  87.     def value(self):
  88.         return self._value

  89.     def reset(self):
  90.         self._value = 0

  91.     def close(self):
  92.         self._hal_close()

  93.     def add_listener(self, l):
  94.         self._listener.append(l)

  95.     def remove_listener(self, l):
  96.         if l not in self._listener:
  97.             raise ValueError('{} is not an installed listener'.format(l))
  98.         self._listener.remove(l)
  99.         
  100.     def _process_rotary_pins(self, pin):
  101.         old_value = self._value
  102.         clk_dt_pins = (self._hal_get_clk_value() <<
  103.                        1) | self._hal_get_dt_value()
  104.                        
  105.         if self._invert:
  106.             clk_dt_pins = ~clk_dt_pins & 0x03
  107.             
  108.         # Determine next state
  109.         if self._half_step:
  110.             self._state = _transition_table_half_step[self._state &
  111.                                                       _STATE_MASK][clk_dt_pins]
  112.         else:
  113.             self._state = _transition_table[self._state &
  114.                                             _STATE_MASK][clk_dt_pins]
  115.         direction = self._state & _DIR_MASK

  116.         incr = 0
  117.         if direction == _DIR_CW:
  118.             incr = self._incr
  119.         elif direction == _DIR_CCW:
  120.             incr = -self._incr

  121.         incr *= self._reverse

  122.         if self._range_mode == self.RANGE_WRAP:
  123.             self._value = _wrap(
  124.                 self._value,
  125.                 incr,
  126.                 self._min_val,
  127.                 self._max_val)
  128.         elif self._range_mode == self.RANGE_BOUNDED:
  129.             self._value = _bound(
  130.                 self._value,
  131.                 incr,
  132.                 self._min_val,
  133.                 self._max_val)
  134.         else:
  135.             self._value = self._value + incr

  136.         try:
  137.             if old_value != self._value and len(self._listener) != 0:
  138.                 _trigger(self)
  139.         except:
  140.             pass
复制代码



设备B代码
main.py
  1. from umqtt.simple import MQTTClient
  2. import urequests as requests
  3. import time
  4. import ujson
  5. import network
  6. from ssd1306 import SSD1306_I2C
  7. from machine import Pin, I2C
  8. import math


  9. device_id = "esp0008"
  10. wifi_ssid='FAST_20CC'
  11. wifi_password='409409409'
  12. mqtt_serverip='192.168.1.113'
  13. mqtt_serverport=1883
  14. mqtt_clientid=device_id
  15. mqtt_publishtopic=b'espiot'
  16. mqttSubTopic=mqtt_publishtopic

  17. def draw_circle():
  18.   x=64
  19.   y=32
  20.   r=30
  21.   angleList = [math.radians(i) for i in range(0, 360)]
  22.   for i in angleList:
  23.     light_dot(x+round(math.sin(i)*r), y+round(math.cos(i)*r))
  24.   oled.show()
  25.   
  26. rotate_list = []
  27. def draw_rotate(angle):
  28.   tmp=math.radians(angle)
  29.   x=64
  30.   y=32
  31.   r=30
  32.   for i in range(0,len(rotate_list)):
  33.     dot = rotate_list[i]
  34.     extinguish_dot(dot[0],dot[1])
  35.   rotate_list.clear()
  36.    
  37.   for i in range(1, 10):
  38.     point_x = x+round(math.sin(tmp)*(r-i))
  39.     point_y = y+round(math.cos(tmp)*(r-i))
  40.     rotate_list.append((point_x,point_y))
  41.     light_dot(point_x,point_y)
  42.   oled.show()
  43.   
  44. def light_dot(x, y):
  45.   oled.pixel(x, y, 1)
  46. def extinguish_dot(x,y):
  47.   oled.pixel(x ,y ,0)


  48. def connectionWifi(ssid, password):
  49.   wlan = network.WLAN(network.STA_IF)
  50.   wlan.active(True)
  51.   wlan.connect(ssid, password)
  52.   while True:
  53.     if not wlan.isconnected():
  54.             print ("connecting...")
  55.     else:
  56.       print('connected to network')
  57.       break
  58.     time.sleep(1)
  59.   
  60. def sub_callback(topic, msg):
  61.   msgStr = msg.decode()
  62.   msgObj = ujson.loads(msgStr)
  63.   if(msgObj['source_device'] != 'esp0007'):
  64.     return
  65.   step_str = msgObj['step']
  66.   step = int(step_str)
  67.   print(step)
  68.   draw_rotate(step)
  69.   

  70.   
  71. def jsonHaveKey(json, key):
  72.   """
  73.   判断json对象中是否包含key
  74.   """
  75.   isHave = True
  76.   try:
  77.     value = json[key]
  78.   except :
  79.     isHave = False
  80.   return isHave

  81. if __name__ == "__main__":
  82.   print ("main funtion")
  83.   
  84.   connectionWifi(wifi_ssid,wifi_password)
  85.   
  86.   client = MQTTClient(mqtt_clientid,mqtt_serverip,mqtt_serverport)
  87.   client.set_callback(sub_callback)
  88.   client.connect()
  89.   print('mqtt client connected')
  90.   
  91.   i2c = I2C(scl=Pin(5), sda=Pin(4))
  92.   oled = SSD1306_I2C(128, 64, i2c)
  93.   draw_circle()

  94.   client.subscribe(mqttSubTopic)
  95.   while True:
  96.     client.wait_msg()
复制代码

项目截图






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 1家元 +60 收起 理由
家睦 + 60

查看全部打赏

发表于 2023-8-30 14:51:46 | 显示全部楼层
不会代码只能看着你们玩
回复 支持 反对

使用道具 举报

发表于 2023-8-30 17:24:04 | 显示全部楼层
这个屏幕的链接可以发一下吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-31 18:35:29 | 显示全部楼层
sadfun 发表于 2023-8-30 17:24
这个屏幕的链接可以发一下吗?

淘宝上搜索"ssd1306",选择IIC接口、白光、0.96寸、焊接排针的就可以了。
https://item.taobao.com/item.htm ... amp;_u=v296iedma2f8
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-10 01:18 , Processed in 1.029601 second(s), 11 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表