Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monitoring TM1628 #45

Closed
mutilator opened this issue Jan 19, 2024 · 6 comments
Closed

Monitoring TM1628 #45

mutilator opened this issue Jan 19, 2024 · 6 comments
Assignees
Labels
question Further information is requested

Comments

@mutilator
Copy link

mutilator commented Jan 19, 2024

Is there any way to use this library to listen to the serial comms. to my TM1628.

Sort of this library in reverse so I can make some of my 'dumb' devices 'smarter'. Read what's displayed on the 7 segments, listen for and make buttons presses on the grid, view other LED that are lit on the device, etc..

I'd like passively listen for everything and interpret the results. Ideally also send button presses from the library as well.

This is an older Royal Sovereign BDH-450 dehumidifier I'd like to remotely monitor and control.

I created fonts for the segments & LED based on playing around.

Segment 0 is the status LED for this device
Segment 1 is the right digit
Segment 2 is the left digit

Segment bitmap

 -- 1 --
|       |
2       0
 -- 3 --
6       7
|       |
 -- 4 --  .5

Segment display maps

byte SEG_DIGITS[] = {
  0b11101011, // 0
  0b10000001, // 1
  0b11011010, // 2
  0b11011001, // 3
  0b10110001, // 4
  0b01111001, // 5
  0b01111011, // 6
  0b11000001, // 7
  0b11111011, // 8
  0b11110001, // 9
  0b11110011, // A
  0b11111011, // B
  0b01101010, // C
  0b11101011, // D
  0b01111010, // E
  0b01110010  // F
};

Status LED for machine

byte STATUS_LIGHTS[] = {
  0b00000001, // FULL RESERVOIR
  0b00000010, // DEFROST
  0b00000100, // ?
  0b00001000, // ?
  0b00010000, // FAN - LOW
  0b00100000, // FAN - MEDIUM
  0b01000000, // FAN - HIGH
  0b10000000, // POWER
};

Using the getbutton listeners it returns the following
Down Arrow - Button 1
Up Arrow - Button 2
Fan Speed - Button 6
Power - Button 7

@mutilator
Copy link
Author

After a bit of playing around I wrote something from scratch so I can read the data but I think emulated button presses are out of the question because I can't respond to the 'are you pressing buttons' command because the TM1628 is also responding.

image

@maxint-rd maxint-rd added the question Further information is requested label Jan 20, 2024
@maxint-rd
Copy link
Owner

maxint-rd commented Jan 20, 2024

Hello, interesting project! It seems really useful to be able to have some remote control to your dehumidifier. My own dehumidifier is not too smart either, so on a daily basis I have to go check it's light to see if the reservoir is full. Would be nice to get a notification on my phone!

So your dehumidifier has a TM1628? In the main readme of this library I'm collecting a list of real world devices using a TM16xx chip. Perhaps you can share a picture of the PCB, showing the chip?

Unfortunately this library only works in one direction and can only be used to drive TM16xx chips. It cannot emulate a TM16xx chip, but you may find it useful to drive a test circuit and analyze its workings. BTW. In issue #37 someone asked a similar question regarding the TM1651 and I gave some more elaborate responses.

Good to see you already implemented something and that you have successfully captured the display data and translated it to a readable form. Well done!

With regard to emulating the button presses, I see some potential. I think this requires additional hardware and cannot be done just in software. You first need to see which of the keyboard lines of the actual TM1628 are connected to the buttons (K1/K2 vs. KS1-KS8). Based on that you would need to solder something to the keyboard lines to make an electrical connection. Once you want to generate a remote button press, you briefly connect these lines to trigger the key-scanning process of the TM1628.

On page 12 of the english datasheet you can find the Application Circuit diagram. Since the TM1628 is meant for common cathode displays, that would suggest that the keyscan checks to see if the KS1-KS8 ouputs are set high by a reading the K1/K2 input lines. Perhaps having a logic mosfet between these will work; eg. between KS1 and K1. Alternatively you could try an opto-coupler, but that may have too high resistance when switched on. Of course you could also use a relais, but those are more bulky/expensive. Before destroying the dehumidifier, I suggest you to first make a test display using some sacrificial TM1628 chips to experiment with.

@maxint-rd maxint-rd self-assigned this Jan 20, 2024
@mutilator
Copy link
Author

Thanks for the quick response. Yea I had tried an opto-coupler and it wasn't working. I ended up using some tiny 3.3v relays and it worked fine, luckily only 4 buttons total.

Here are the images of both sides of the board.
2024-01-20 15 43 20
2024-01-20 15 43 37

Also here is the code I used to output the screenshot above in-case anyone else finds this later and wants to play.

It's running on an ESP-32 module

// Datasheet for TM1628 - https://aitendo3.sakura.ne.jp/aitendo_data/product_img/ic/LED-driver/TM1628english.pdf
#define PIN_DATA    26
#define PIN_CLOCK   18
#define PIN_STROBE  19


byte SEG_DIGITS[] = {
  0b11101011, // 0
  0b10000001, // 1
  0b11011010, // 2
  0b11011001, // 3
  0b10110001, // 4
  0b01111001, // 5
  0b01111011, // 6
  0b11000001, // 7
  0b11111011, // 8
  0b11110001, // 9
  0b11110011, // A
  0b11111011, // B
  0b01101010, // C
  0b11101011, // D
  0b01111010, // E
  0b01110010  // F
};

byte BIT_POWER = 0b10000000;
byte BIT_FAN_HIGH = 0b01000000;
byte BIT_FAN_MEDIUM = 0b00100000;
byte BIT_FAN_LOW = 0b00010000;
byte BIT_DEFROST = 0b00000010;
byte BIT_TANK_FULL = 0b00000001;


bool isStrobeActive = false; // If stroibe is active we're reading data
bool processByte = false; // Whether the main loop should process the data read

int bitsRead = 0; // how many bits read so far, we kick it at 8 no matter what
byte myData = 0; // Last data byte read from the serial
byte waitByteIdx = 0; // which byte of the 6 are we processing
bool gettingDisplay = false; // if we're parsing the 0xC0 command that pushes data to the display
byte aryIdx = 0; // where we're writing to the array
byte byteCache[5]; // small array in case we get data faster than we're processing it

int digitOnes = 0;
int digitTens = 0;
int powerOn = false;
int fanSpeed = 0; //0 = auto, 1 = low, 2 = med, 3 = high
int tankFull = false;
int defrosting = false;

void IRAM_ATTR strobeActive() {
  if (!digitalRead(PIN_STROBE))
  {
    isStrobeActive = true;
  } else {
    isStrobeActive = false;
  }
}


void IRAM_ATTR clockRead() {
  if (!isStrobeActive)
    return;

  bitsRead++;
  myData >>= 1;
  if (digitalRead(PIN_DATA))
    myData |= 0x80;
  
  if (bitsRead % 8 == 0)
  {
    byteCache[aryIdx] = myData;
    aryIdx++;
    processByte = true;
  }
}


void setup()
{
  Serial.begin(115200);
  pinMode(PIN_DATA, INPUT);
  pinMode(PIN_CLOCK, INPUT);
  pinMode(PIN_STROBE, INPUT);

  attachInterrupt(PIN_STROBE, strobeActive, CHANGE);
  attachInterrupt(PIN_CLOCK, clockRead, RISING);
}


void loop () {
  processIncomingData();
  if (millis() % 5000 == 0) // show something roughly every 5 seconds
    printStatus()
}

void printStatus()
{
  static char fs[7];
    static char pw[4];
    static char tf[6];
    static char df[4];
    switch (fanSpeed)
    {
      case 0:
        strcpy(fs,"Auto");
        break;
      case 1:
        strcpy(fs,"Low");
        break;
      case 2:
        strcpy(fs,"Medium");
        break;
      case 3:
        strcpy(fs,"High");
        break;
    }

    if (powerOn)
      strcpy(pw, "On");
    else
      strcpy(pw, "Off");

    if (tankFull)
      strcpy(tf, "Full");
    else
      strcpy(tf, "Empty");

    if (defrosting)
      strcpy(df, "Yes");
    else
      strcpy(df, "No");

    Serial.print("Current Humidity: ");
    Serial.print(digitTens);
    Serial.println(digitOnes);
    Serial.print("Power: ");
    Serial.println(pw);
    Serial.print("Fan Speed: ");
    Serial.println(fs);
    Serial.print("Water Reservoir: ");
    Serial.println(tf);
    Serial.print("Defrosting: ");
    Serial.println(df);
    Serial.println("");
    Serial.println("");
}


void processIncomingData()
{
  if (processByte)
  {
    for(int i = aryIdx - 1; i >= 0; i--)
    {
      if (gettingDisplay)
      {
        switch (waitByteIdx)
        {
          case 0: // LED STATUS_LIGHTS
            powerOn = (byteCache[i] & BIT_POWER) == BIT_POWER;
            fanSpeed = (byteCache[i] & BIT_FAN_HIGH) == BIT_FAN_HIGH?3:(byteCache[i] & BIT_FAN_MEDIUM) == BIT_FAN_MEDIUM?2:(byteCache[i] & BIT_FAN_LOW) == BIT_FAN_LOW?1:0;
            tankFull = (byteCache[i] & BIT_TANK_FULL) == BIT_TANK_FULL;
            defrosting = (byteCache[i] & BIT_DEFROST) == BIT_DEFROST;
            break;
          case 1: // empty bit? could be an issue with how we're reading data
            break;
          case 2:
            digitOnes = getDigit(byteCache[i]);
            break;
          case 3: // empty bit? could be an issue with how we're reading data
            break;
          case 4:
            digitTens = getDigit(byteCache[i]);
            break;
          case 5: // empty bit? could be an issue with how we're reading data
            gettingDisplay = false; // This is the last empty bit from the 0xC0
            break;
        }
        waitByteIdx++;
      }
      if (byteCache[i] == 0xC0) // Command 3 from datasheet
      {
        waitByteIdx = 0;
        gettingDisplay = true; // Start interpreting the following bytes as segment display
      }
      //Serial.println(byteCache[i],HEX);
    }
    aryIdx = 0;
    
    processByte = false;
  }
}

byte getDigit(byte myDigit)
{
  for(int i = 0; i < 9; i++)
    if (myDigit == SEG_DIGITS[i])
      return i;

  return 0; //uh give em something i guess?
}

maxint-rd added a commit that referenced this issue Jan 21, 2024
Added link to issue #45 about the TM1628 in a Royal Sovereign BDH-450 dehumidifier
Also added some text to the other links.
@maxint-rd
Copy link
Owner

Very nice project with great success. Congratulations !
Thank you for sharing the pictures and your code. I've added a link to this issue in the main readme so that your contribution is highlighted and the issue can be closed.

@mutilator
Copy link
Author

mutilator commented Jan 23, 2024

I turned this into an ESPhome component integration here: https://github.com/mutilator/esphome/tree/dev/esphome/components/bdh450

Might give someone a bit of a start on a similar path.

@maxint-rd
Copy link
Owner

maxint-rd commented Jan 23, 2024

Very nice! Good example of how to make a device smart and integrate it in your household. Thank you for sharing your code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants