├── .gitignore
├── CMakeLists.txt
├── README.md
├── snap
└── snapcraft.yaml
└── src
├── devices.h
├── lib
├── HIDDCDCUSB.cpp
├── HIDDCDCUSB.h
├── HIDInterface.cpp
├── HIDInterface.h
├── HIDNUCUPS.cpp
├── HIDNUCUPS.h
├── HIDOpenUPS.cpp
├── HIDOpenUPS.h
├── HIDOpenUPS2.cpp
├── HIDOpenUPS2.h
├── usbhid.cpp
└── usbhid.h
├── main.cpp
├── utils
├── HArray.cpp
├── HArray.h
├── HList.cpp
├── HList.h
├── HLock.cpp
├── HLock.h
├── HThread.cpp
├── HThread.h
├── TxFile.cpp
├── TxFile.h
├── util.cpp
└── util.h
└── version.h
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 | build/
3 | *.swp
4 | *.ninja
5 | .ninja*
6 | CMakeCache.txt
7 | CMakeFiles
8 | cmake_install.cmake
9 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(openups CXX C)
3 |
4 | set(SOURCES
5 | src/main.cpp
6 | src/lib/usbhid.cpp
7 | src/lib/HIDOpenUPS.cpp
8 | src/lib/HIDOpenUPS2.cpp
9 | src/lib/HIDNUCUPS.cpp
10 | src/lib/HIDDCDCUSB.cpp
11 | src/lib/HIDInterface.cpp
12 | src/utils/util.cpp
13 | src/utils/HArray.cpp
14 | )
15 |
16 | option(_DEBUG "Debug messages" ON)
17 | if (_DEBUG)
18 | add_definitions(-D_DEBUG)
19 | endif()
20 |
21 | add_executable(openups ${SOURCES})
22 |
23 |
24 | target_include_directories(openups PUBLIC src/utils/ src/lib/)
25 | target_link_libraries(openups
26 | #pthread
27 | usb
28 | )
29 |
30 | install(
31 | TARGETS openups
32 | DESTINATION bin
33 | )
34 |
35 | # Snap Build
36 | # snapcraft clean
37 | # snapcraft
38 | # snapcraft upload --release=beta openups_0.4_amd64.snap
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Mini-Box.com UPS Status and Configuration Tool
3 |
4 | Mini-Box.com produces several high quality intelligent Uninterruptible Power Supplies. This is a tool to report status information and configure these UPS models.
5 |
6 | ## Supported models
7 |
8 | - **OpenUPS**: 6-30V Intelligent Uninterruptible Power Supply
9 | - **OpenUPS2**: 11-24V Intelligent DC-DC Uninterruptible Power Supply using LiFePO4 or 13-24V using Li-Ion (see manual)
10 | - **NUC-UPS**: 6-38V Intelligent Automotive grade Uninterruptible Power Supply
11 | - **DCDC-USB**: Intelligent buck-boost DC-DC converter with USB interface
12 | - **DCDC-USB-200**: Intelligent buck-boost DC-DC converter with USB interface
13 |
14 | ## Warning
15 | The flash storage on these devices have limited write cycles (should be considered < _100.000_) so it's *not* advisable to keep writing the configuration periodically on a timer.
16 |
17 | ## Comparison table
18 | | Feature | [OpenUPS](https://www.mini-box.com/OpenUPS) | [OpenUPS2](https://www.mini-box.com/OpenUPS2) | [NUC-UPS](https://www.mini-box.com/NUC-UPS) |
19 | |---------|---------|----------|---------|
20 | | Picture |
|
|
|
21 | |Input Voltage | 6-30V| 11-24V| 6-38 |
22 | |Output Voltage| 5-24V (programable)| 12V* | 12V|
23 | |Architecture| Buck/Boost| Boost| Buck/Boost|
24 | |Max Power| 6A| 5A| 5A|
25 | |Battery Chemistry| Li-Ion, Li-Po, LIFEPO4, Lead Acid| LiFePO4, Li-Ion** | Li-Ion
26 | |Battery Charger| Multiple chemistry charger| 3 state charger| 3 state charger|
27 | |Battery balancing| Yes |Yes| Yes|
28 | |USB Interface| Yes| Yes| Yes|
29 | |SMBUS| Slave| Slave| -|
30 | |Coulomb counting (fuel gauge)| Yes| Yes| Yes|
31 | |Temperature monitoring| On board| For each cell| For each cell|
32 | |Battery design| External removable| Onboard removable| Onboard removable|
33 | |Footprint| Custom| 2.5" drive footprint| 2.5" drive footprint|
34 | |Windows compatible| Yes| Yes| Yes|
35 | |Linux compatible| Yes| Yes | Yes|
36 | |Motherboard ON/OFF pulse control| Yes| Yes| Yes|
37 | Deep Sleep power consumption| <50uA| 1uA |5uA|
38 | |Availability| Long life| Long life| New product|
39 |
40 | ### Notes:
41 | * If V(in) > V(out) then V(out) is unregulated (Vin=vout)
42 | * If V(in) < V(out) then V(out) is 12V regulated
43 | ** Li-Ion chemistry supported with hardware modification, consult manual.
44 |
45 | ## Documentation
46 |
47 | These devices allow advanced configuration which can be quite complex. It's recommended to read the documentation using the links below before attempting to configure these UPSes.
48 |
49 | - [OpenUPS ](https://resources.mini-box.com/online/PWR-OpenUPS/PWR-OpenUPS-hardware-manual.pdf)
50 | - [OpenUPS2 ](http://wiki.mini-box.com/index.php?title=OpenUPS2)
51 | - [NUC-UPS ](http://wiki.mini-box.com/index.php?title=NUC-UPS)
52 | - [DCDC-USB](https://resources.mini-box.com/online/PWR-DCDC-USB/PWR-DCDC-USB-Advanced-USB-configuration-manual.pdf)
53 | - [DCDC-USB-200](https://resources.mini-box.com/online/PWR-DCDC-USB-200/PWR-DCDC-USB-200-Advanced-USB-configuration-manual.pdf)
54 |
55 |
56 | ## Install from prebuilt binary
57 |
58 | $ sudo snap install openups --edge
59 |
60 | [](https://snapcraft.io/openups)
61 |
62 | ([Don't have snapd installed?](https://snapcraft.io/docs/core/install))
63 |
64 | ## Install from source
65 |
66 | $ git clone https://github.com/mini-box/ups.git
67 | $ cd ups
68 | $ cmake .
69 | $ sudo make install
70 |
71 |
72 | ## Using
73 | For all commands below replace *device name* with the name of the device you want to control eg: *openups*, *openups2*, *nucups*
74 |
75 | - Show help, supported devices and supported commands per device:
76 |
77 | $ openups -h
78 |
79 | - Show status and configuration:
80 |
81 | $ sudo openups -t
82 | - Show only status
83 |
84 | $ sudo openups -t -s
85 | - Export current configuration variables from device to file
86 |
87 | $ sudo openups -t -o
88 |
89 | - Import configuration from file to device.
90 | **Warning: it will restart your UPS and in certain cases the device connected to it !**
91 |
92 | $ sudo openups -t -i
93 |
94 | - For configuration variables you can add ```-c ``` switch on command line to output a detailed description for each variable. Example:
95 | ```6. UPS_INIT_DELAY_TOUT: 1 # [s] Initial delay before starting the UPS. Default is 1 sec. ```
96 |
97 | - Some devices accept commands to set for example the output voltage:
98 |
99 | $ sudo openups -t dcdcusb -e set_vout:11.5 -s
100 |
101 |
102 | # Troubleshooting
103 |
104 | There might be issues with apparmor/SELinux that won't allow access to raw usb devices.
105 | For the snap binary this can be solved by:
106 |
107 | $ sudo snap connect openups:raw-usb
108 |
109 | # Network UPS Tools
110 |
111 | For remote management or shutdown configuration it's also possible to use [Network UPS Tools](https://networkupstools.org/). Open UPS and Open UPS 2 are already
112 | integrated in latest version so installation should be straight forward on different Linux flavors.
113 | For more details on custom installations and configuration for our UPS products on [Network UPS Tools](https://networkupstools.org/) see these links:
114 | - [Open UPS](https://github.com/mini-box/openups)
115 | - [Open UPS 2](https://github.com/mini-box/openups2)
116 |
--------------------------------------------------------------------------------
/snap/snapcraft.yaml:
--------------------------------------------------------------------------------
1 | name: openups
2 | base: core20 # the base snap is the execution environment for this snap
3 | version: 'git' # just for humans, typically '1.2+git' or '1.3.2'
4 | version-script: cat src/version.h | grep VERSION | cut -f 3 -d " " | tr -d '"'
5 | summary: Control app for Mini-Box.com UPS products
6 | description: Allows displaying the status and configuring the settings for all UPS products from Mini-Box.com. These include Open UPS, Open UPS2, NUC UPS and more.
7 |
8 | grade: devel # must be 'stable' to release into candidate/stable channels
9 | confinement: strict # devmode # use 'strict' once you have the right plugs and slots
10 |
11 | apps:
12 | openups:
13 | plugs: [raw-usb]
14 | command: usr/local/bin/openups
15 | parts:
16 | openups:
17 | # See 'snapcraft plugins'
18 | plugin: cmake
19 | source: .
20 | source-type: local
21 | build-packages:
22 | - g++
23 | - make
24 | - libusb-0.1-4
25 | - libusb-dev
26 | stage-packages:
27 | - libusb-0.1-4
--------------------------------------------------------------------------------
/src/devices.h:
--------------------------------------------------------------------------------
1 | #define MINIBOX_VENDOR_ID 0x04d8
2 |
3 | #define DCDCUSB_PRODUCT_ID 0xd003
4 | #define OPENUPS_PRODUCT_ID 0xd004
5 | #define OPENUPS2_PRODUCT_ID 0xd005
6 | #define NUCUPS_PRODUCT_ID 0xd007
7 |
8 | struct deviceId {
9 | const char *name;
10 | const char *desc;
11 | const unsigned int vendorid;
12 | const unsigned int productid;
13 | const unsigned int max_transfer_size;
14 | const struct deviceCmd *cmds;
15 | };
16 |
17 | struct deviceCmd {
18 | const char *cmd;
19 | const char *desc;
20 | };
21 |
22 | static const struct deviceCmd dcdc_usb_cmds[] = {
23 | { "set_vout", "Set Output Voltage" },
24 | {}
25 | };
26 |
27 | static const struct deviceId deviceIds[] = {
28 | {
29 | "dcdcusb",
30 | "DCDC USB",
31 | MINIBOX_VENDOR_ID,
32 | DCDCUSB_PRODUCT_ID,
33 | 24,
34 | dcdc_usb_cmds
35 | },
36 | {
37 | "openups",
38 | "Open UPS",
39 | MINIBOX_VENDOR_ID,
40 | OPENUPS_PRODUCT_ID,
41 | 32,
42 | {}
43 | },
44 | {
45 | "openups2",
46 | "Open UPS2",
47 | MINIBOX_VENDOR_ID,
48 | OPENUPS2_PRODUCT_ID,
49 | 32,
50 | {}
51 | },
52 | {
53 | "nucups",
54 | "DC-DC NUC UPS",
55 | MINIBOX_VENDOR_ID,
56 | NUCUPS_PRODUCT_ID,
57 | 32,
58 | {}
59 | },
60 | {}
61 | };
62 |
--------------------------------------------------------------------------------
/src/lib/HIDDCDCUSB.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | *
7 | */
8 |
9 | #include "HIDDCDCUSB.h"
10 | #include "util.h"
11 | #include
12 |
13 | #define A_SLEEP 5
14 | #define A_TIMEOUT 3000
15 | #define BUF_LINE_LEN 47
16 | #define STR_LINE_LEN 57
17 |
18 | #define PKG_LEN_LCD 64
19 | #define PKG_LEN_FLASH 37
20 |
21 | unsigned int g_DCDCUSB_memTerm[] = {(unsigned int)0x01};
22 |
23 | ATXMSG g_DCDCUSB_memMessages[DCDCUSB_MAX_MESSAGE_CNT] =
24 | {
25 | {52, 1, _T("VOLTAGE_0"), true, 7, 0, 0, 0, _T("Set Output Voltage 0. Changes will take effect after a power cycle."), _T("[V]")},
26 | {53, 1, _T("VOLTAGE_1"), true, 7, 0, 0, 0, _T("Set Output Voltage 1. Changes will take effect after a power cycle."), _T("[V]")},
27 | {54, 1, _T("VOLTAGE_2"), true, 7, 0, 0, 0, _T("Set Output Voltage 2. Changes will take effect after a power cycle."), _T("[V]")},
28 | {55, 1, _T("VOLTAGE_3"), true, 7, 0, 0, 0, _T("Set Output Voltage 3. Changes will take effect after a power cycle."), _T("[V]")},
29 | {56, 1, _T("VOLTAGE_4"), true, 7, 0, 0, 0, _T("Set Output Voltage 4. Changes will take effect after a power cycle."), _T("[V]")},
30 | {57, 1, _T("VOLTAGE_5"), true, 7, 0, 0, 0, _T("Set Output Voltage 5. Changes will take effect after a power cycle."), _T("[V]")},
31 | {58, 1, _T("VOLTAGE_6"), true, 7, 0, 0, 0, _T("Set Output Voltage 6. Changes will take effect after a power cycle."), _T("[V]")},
32 | {59, 1, _T("VOLTAGE_7"), true, 7, 0, 0, 0, _T("Set Output Voltage 7. Changes will take effect after a power cycle."), _T("[V]")},
33 |
34 | {0, 1, _T("IGNITION_HIGH_LIMIT"), true, 2, 0.1558, 0, 0, _T("Above this voltage IGN is considered ON"), _T("[V]")},
35 | {1, 1, _T("IGNITION_LOW_LIMIT"), true, 2, 0.1558, 0, 0, _T("Below this voltage IGN is considered OFF"), _T("[V]")},
36 | {2, 1, _T("IGNITION_DEBOUNCE"), false, 1, 10, 0, 0, _T("Ignition filter de-bouncing"), _T("[ms]")},
37 |
38 | {30, 1, _T("AUTOMOTIVE_IGNITION_CANCEL_TIME"), true, 1, 1, 0, 0, _T("After DCDC-USB \"boots\" in AUTOMOTIVE mode IGN is cancelled for this time"), _T("[sec]")},
39 | {14, 1, _T("AUTOMOTIVE_THUMP_TIMEOUT"), true, 1, 1, 0, 0, _T("THUMP timeout, switching off the audio amplifier during motherboard startup/shutdown"), _T("[sec]")},
40 |
41 | {3, 1, _T("DELAY_BEFORE_PSU_STARTUP"), true, 1, 1, 0, 0, _T("Initial startup delay to avoid re-cranking problems"), _T("[s]")},
42 | {4, 1, _T("MIN_VIN_VOLTAGE_AT_STARTUP"), true, 2, 0.1558, 0, 0, _T("Input voltage STARTUP ZONE treshold "), _T("[V]")},
43 | {5, 1, _T("MIN_VIN_VOLTAGE_WHILE_RUNNING"), true, 2, 0.1558, 0, 0, _T("Input voltage RUNNING ZONE treshold "), _T("[V]")},
44 | {6, 1, _T("MAX_ALLOWED_VIN_VOLTAGE"), false, 2, 0.1558, 0, 0, _T("Input voltage, HIGH SHUTDOWN treshold [35V]"), _T("[V]")},
45 |
46 | {63, 1, _T("MIN_VIN_VOLTAGE_DEEP_DISCHARGE"), true, 2, 0.1558, 0, 0, _T("Minimum VIN voltage measured during off delay"), _T("[V]")},
47 |
48 | {7, 1, _T("ON_PULSE_DELAY"), true, 1, 1, 0, 0, _T("Delay between output is ON and startup pulse is sent\n(v4.3+)"), _T("[s]")},
49 | // { 8, 1, _T("Min. limit for Voltage") , true, 2, 0.1558,0,0 , _T("MIN voltage treshold"), _T("[V]") },
50 | {9, 1, _T("REGULATOR_OFFSET"), true, 1, 1, 0, 0, _T("Regulator offset (firmware version >= v2.0)"), _T("")},
51 | {10, 2, _T("PSW_PUSH_DOWN_TIME"), true, 1, 10, 0, 0, _T("How long the PSW button is pressed down"), _T("[ms]")},
52 | //{11, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
53 | // { 12, 1, _T("Emergency timeout") , true, 1, 1,0,0 , _T("Delay"), _T("[s]") },
54 | {13, 1, _T("CONFIGURATION_FLAGS"), true, 9, 1, 0, 0, _T("Configuration flags: bit0=Regulator Enabled/Disabled(v2.0+)\nbit1=Power cycle On/Off(v2.1+)\nbit2=On pulse enable/disable bit3=Off pulse enable/disable(v3.1+)\nbit4=USB sense enable/disable(v4.0+)\nbit5=Ignition pulse enable/disable(v4.2+)"), _T("")},
55 | {15, 1, _T("MAX_REGULATION_STEP_NUMBER"), true, 1, 1, 0, 0, _T("Maximum allowed regulation step number (0-255) (firmware version >= v2.0)"), _T("")},
56 |
57 | {16, 2, _T("OFF_DELAY_TIME_0"), false, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 0 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
58 | //{17, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
59 | {18, 2, _T("HARD_OFF_TIME_0"), false, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 0 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
60 | //{19, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
61 | {20, 2, _T("OFF_DELAY_TIME_1"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 1 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
62 | //{21, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
63 | {22, 2, _T("HARD_OFF_TIME_1"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 1 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
64 | //{23, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
65 | {24, 2, _T("OFF_DELAY_TIME_2"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 2 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
66 | //{25, 0, _T("FILLER") , true, 1, 1 , _T(""), _T("") },
67 | {26, 2, _T("HARD_OFF_TIME_2"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 2 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
68 | //{27, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
69 | {28, 2, _T("OFF_DELAY_TIME_3"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 3 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
70 | //{29, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
71 |
72 | {32, 2, _T("HARD_OFF_TIME_3"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 3 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
73 | //{33, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
74 | {34, 2, _T("OFF_DELAY_TIME_4"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 4 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
75 | //{35, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
76 | {36, 2, _T("HARD_OFF_TIME_4"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 4 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
77 | //{37, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
78 | {38, 2, _T("OFF_DELAY_TIME_5"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 5 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
79 | //{39, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
80 | {40, 2, _T("HARD_OFF_TIME_5"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 5 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
81 | //{41, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
82 | {42, 2, _T("OFF_DELAY_TIME_6"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 6 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
83 | //{43, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
84 | {44, 2, _T("HARD_OFF_TIME_6"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 6 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
85 | //{45, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
86 |
87 | {48, 2, _T("OFF_DELAY_TIME_7"), true, 3, 1, 0, 0, _T("Time between IGN OFF to PSW OFF impulse in Time Configuration 7 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
88 | //{49, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
89 | {50, 2, _T("HARD_OFF_TIME_7"), true, 3, 1, 0, 0, _T("Time Between PSW OFF to Output OFF,Hardware OFF in Time Configuration 7 (max. 17:59:59 or Never)"), _T("[hh:mm:ss]")},
90 | //{51, 0, _T("FILLER") , true, 1, 1,0,0 , _T(""), _T("") },
91 |
92 | {46, 1, _T("UPS_MODE_SHUTDOWN_TIME"), true, 1, 1, 0, 0, _T("After OFF impulse is sent wait \"UPS Mode: Shutdown Time\" till Output OFF,Hardware OFF"), _T("[sec]")},
93 | {47, 1, _T("UPS_MODE_IGNITION_OFF_TIME"), true, 1, 1, 0, 0, _T("If IGN is OFF for \"UPS Mode: Ignition OFF time\" time the PSU will send OFF impulse"), _T("[sec]")},
94 |
95 | {60, 1, _T("UPS_MODE_IGNITION_ON"), true, 2, 0.1558, 0, 0, _T("PSU will be ON if IGN>\"UPS Mode: Ignition ON\" (and VIN>\"UPS Mode: VIn Good\")"), _T("[V]")},
96 | {61, 1, _T("UPS_MODE_IGNITION_OFF"), true, 2, 0.1558, 0, 0, _T("PSU will be OFF if IGN<\"UPS Mode: Ignition OFF\" for \"UPS Mode: Ignition OFF time\" seconds"), _T("[V]")},
97 | {62, 1, _T("UPS_MODE_VIN_GOOD"), true, 2, 0.1558, 0, 0, _T("PSU will be ON if VIN>\"UPS Mode: VIn Good\" (and IGN>\"UPS Mode: Ignition ON\")"), _T("[V]")},
98 | };
99 |
100 | double HIDDCDCUSB::GetVOut(unsigned char data)
101 | {
102 | double rpot = ((double)data) * DCDCUSB_CT_RP / (double)257 + DCDCUSB_CT_RW;
103 | double voltage = (double)80 * ((double)1 + DCDCUSB_CT_R1 / (rpot + DCDCUSB_CT_R2));
104 | voltage = floor(voltage);
105 | return voltage / 100;
106 | }
107 |
108 | unsigned char HIDDCDCUSB::ConvertVoltageToChar(double vout)
109 | {
110 | if (vout < 5)
111 | vout = 5;
112 |
113 | if (vout > 25)
114 | vout = 25;
115 |
116 | double rpot = (double)0.8 * DCDCUSB_CT_R1 / (vout - (double)0.8) - DCDCUSB_CT_R2;
117 | double result = (257 * (rpot - DCDCUSB_CT_RW) / DCDCUSB_CT_RP);
118 |
119 | if (result < 0)
120 | result = 0;
121 | if (result > 255)
122 | result = 255;
123 |
124 | return (unsigned char)result;
125 | }
126 |
127 | HIDDCDCUSB::HIDDCDCUSB(USBHID *d) : HIDInterface(d)
128 | {
129 | m_ulSettingsAddr = DCDCUSB_SETTINGS_ADDR_START;
130 | }
131 |
132 | HIDDCDCUSB::~HIDDCDCUSB()
133 | {
134 | }
135 |
136 | ATXMSG *HIDDCDCUSB::GetMessages()
137 | {
138 | return g_DCDCUSB_memMessages;
139 | }
140 |
141 | double HIDDCDCUSB::GetConstant(int i)
142 | {
143 | return 1.0;
144 | }
145 |
146 | unsigned int *HIDDCDCUSB::GetTermistorConsts()
147 | {
148 | return g_DCDCUSB_memTerm;
149 | }
150 |
151 | void HIDDCDCUSB::printValues()
152 | {
153 | fprintf(stdout, "Mode: %d %s\n", m_nMode, m_strMode);
154 | fprintf(stdout, " Configured Voltage Ouput: %.02f\n", m_fVOutSet);
155 | fprintf(stdout, " Voltage Config: %d\n V In: %.02f\n V Ign: %.02f\n V Out: %.02f\n", m_nVoltageCfg, m_fVIn, m_fIgn, m_fVOut);
156 | fprintf(stdout, " Power Switch: %d\n Output: %d\n Aux V In: %d\n", m_bPowerSwitch, m_bOutput, m_bAuxVin);
157 | fprintf(stdout, " Version: %d.%d\n State: 0x%02x\n", m_nVerMajor, m_nVerMinor, m_nState);
158 | fprintf(stdout, " Voltage (flags): %s\n Status 1 (flags): %s\n Status 2 (flags): %s\n", m_strFlagsVoltage, m_strFlagsStatus1, m_strFlagsStatus2);
159 | fprintf(stdout, " Timer (flags): %s\n Timer Config: 0x%02x\n Voltage Config: %s\n", m_strFlagsTimer, m_nTimeCfg, m_strVoltCfg);
160 | fprintf(stdout, " Timer Wait: %s\n Timer V Out: %s\n Timer PW Switch: %s\n", m_strTimerWait, m_strTimerVout, m_strTimerPwSwitch);
161 | fprintf(stdout, " Timer V Aux: %s\n Timer Off Delay: %s\n Timer Hard Off: %s\n", m_strTimerVAux, m_strTimerOffDelay, m_strTimerHardOff);
162 | }
163 |
164 | void HIDDCDCUSB::parseMessage(unsigned char *msg)
165 | {
166 | if (!msg)
167 | return;
168 |
169 | switch (msg[0])
170 | {
171 | case DCDCUSB_INTERNAL_MESG:
172 | {
173 | switch (msg[1])
174 | {
175 | case DCDCUSB_INTERNAL_MESG_DISCONNECTED:
176 | {
177 | fprintf(stderr, "DCDCUSB DEVICE DISCONNECTED!");
178 | }
179 | break;
180 | }
181 | }
182 | break;
183 | case DCDCUSB_RECV_ALL_VALUES:
184 | {
185 | m_nState = msg[2];
186 | m_nTimeCfg = (msg[1] >> 5) & 0x7;
187 | m_nVoltageCfg = (msg[1] >> 2) & 0x7;
188 |
189 | float val;
190 | //VIN
191 | m_fVIn = (float)msg[3] * (float)0.1558;
192 |
193 | //Ign
194 | m_fIgn = (float)msg[4] * (float)0.1558;
195 |
196 | //VOut
197 | m_fVOut = (float)msg[5] * (float)0.1170;
198 |
199 | int status1 = msg[6];
200 | if (status1 & 0x4)
201 | m_bPowerSwitch = true;
202 | else
203 | m_bPowerSwitch = false;
204 |
205 | if (status1 & 0x8)
206 | m_bOutput = true;
207 | else
208 | m_bOutput = false;
209 |
210 | if (status1 & 0x10)
211 | m_bAuxVin = true;
212 | else
213 | m_bAuxVin = false;
214 |
215 | char2bin(m_strFlagsStatus1, msg[6]);
216 | char2bin(m_strFlagsStatus2, msg[7]);
217 | char2bin(m_strFlagsVoltage, msg[8]);
218 | char2bin(m_strFlagsTimer, msg[9]);
219 |
220 | m_nScriptPointer = msg[10];
221 |
222 | convertOneValue2String(m_strTimerWait, 2, 11, 3, 1);
223 | convertOneValue2String(m_strTimerVout, 2, 13, 3, 1);
224 | convertOneValue2String(m_strTimerVAux, 2, 15, 3, 1);
225 | convertOneValue2String(m_strTimerPwSwitch, 2, 17, 3, 1);
226 | convertOneValue2String(m_strTimerOffDelay, 2, 19, 3, 1);
227 | convertOneValue2String(m_strTimerHardOff, 2, 21, 3, 1);
228 |
229 | m_bVersion = msg[23];
230 | m_nVerMajor = (msg[23] >> 5) & 0x07;
231 | m_nVerMinor = msg[23] & 0x1F;
232 | m_nMode = (msg[1]) & 0x3;
233 |
234 | switch ((msg[1]) & 0x3)
235 | {
236 | case 0:
237 | strcpy(m_strMode, "(Dumb)");
238 | break;
239 | case 1:
240 | strcpy(m_strMode, "(Automotive)");
241 | break;
242 | case 2:
243 | strcpy(m_strMode, "(Script)");
244 | break;
245 | case 3:
246 | strcpy(m_strMode, "(UPS)");
247 | break;
248 | }
249 | }
250 | break;
251 | case DCDCUSB_CMD_IN:
252 | {
253 | if (msg[1] != 0)
254 | {
255 | if (msg[2] == DCDCUSB_CMD_READ_REGULATOR_STEP)
256 | fprintf(stdout, "Regulator: undefined");
257 | else
258 | fprintf(stderr, "Error %d for message %02X", msg[1], msg[2]);
259 | }
260 | else
261 | {
262 | switch (msg[2])
263 | {
264 | case DCDCUSB_CMD_WRITE_VOUT:
265 | case DCDCUSB_CMD_READ_VOUT:
266 | case DCDCUSB_CMD_INC_VOUT:
267 | case DCDCUSB_CMD_DEC_VOUT:
268 | {
269 | m_fVOutSet = HIDDCDCUSB::GetVOut(msg[3]);
270 | }
271 | break;
272 | case DCDCUSB_CMD_READ_REGULATOR_STEP:
273 | {
274 | fprintf(stdout, "Regulator Step: %d", msg[3]);
275 | }
276 | break;
277 | }
278 | }
279 | }
280 | break;
281 | case DCDCUSB_MEM_READ_IN:
282 | {
283 | //TX[24]:A1 C0 3F 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
284 | //RX[24]:A2 C0 3F 00 10 26 24 32 05 26 23 E0 AA 00 03 00 32 0A 00 AA AA 00 00 01
285 | memcpy(m_chPackages + (m_ulSettingsAddr - DCDCUSB_SETTINGS_ADDR_START), msg + 5, 16);
286 | }
287 | break;
288 | }
289 | }
290 |
291 | float HIDDCDCUSB::convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier)
292 | {
293 | double value = -1;
294 |
295 | if (nLen == 1)
296 | {
297 | value = (double)buffer[nIndex] * dMultiplier;
298 | }
299 | else
300 | {
301 | value = (double)((((int)buffer[nIndex]) << 8) | buffer[nIndex + 1]) * dMultiplier;
302 | }
303 |
304 | switch (nReadMode)
305 | {
306 | case 5:
307 | {
308 | if (nLen == 1)
309 | value = (double)((char)buffer[nIndex]) * dMultiplier;
310 | else
311 | value = (double)((((int)((char)buffer[nIndex])) << 8) | buffer[nIndex + 1]) * dMultiplier;
312 | }
313 | break;
314 |
315 | case 7:
316 | {
317 | //no multiplier - voltage value calculated with formula
318 | value = HIDDCDCUSB::GetVOut(buffer[nIndex]);
319 | }
320 | break;
321 | }
322 |
323 | return (float)value;
324 | }
325 |
326 | bool HIDDCDCUSB::readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4)
327 | {
328 | bool ok = false;
329 | c1 = 0;
330 | c2 = 0;
331 |
332 | double val = 0;
333 |
334 | switch (nReadMode)
335 | {
336 | case 3:
337 | { //hh:mm:ss
338 | if (strcmp(str, "Never") == 0)
339 | {
340 | val = 0xFFFF;
341 | ok = true;
342 | }
343 | else if ((strlen(str) == 8) && (str[2] == ':') && (str[5] == ':'))
344 | {
345 | _TCHAR *end = NULL;
346 | char temps[10];
347 | str_left(temps, str, 2);
348 | int hh = _tcstol(temps, &end, 10);
349 | if ((end != temps) && (hh >= 0) && (hh < 18))
350 | {
351 | str_mid(temps, str, 3, 2);
352 | int mm = _tcstol(temps, &end, 10);
353 | if ((end != temps) && (mm >= 0) && (mm < 60))
354 | {
355 | str_right(temps, str, 2);
356 | int ss = _tcstol(temps, &end, 10);
357 |
358 | if ((end != temps) && (ss >= 0) && (ss < 60))
359 | {
360 | val = hh * 3600 + mm * 60 + ss;
361 | ok = true;
362 | }
363 | }
364 | }
365 | }
366 | }
367 | break;
368 | case 4:
369 | { //"signed" float
370 | if (strlen(str) != 0)
371 | {
372 | bool negative = false;
373 | char temps[10];
374 | char temps2[10];
375 |
376 | if (str[0] == '-')
377 | {
378 | str_right(temps, str, strlen(str) - 1);
379 | str = temps;
380 | negative = true;
381 | }
382 |
383 | _TCHAR *end = NULL;
384 | str_left(temps2, str, 8);
385 | val = _tcstod(temps2, &end);
386 |
387 | if ((end != temps2))
388 | {
389 | ok = true;
390 |
391 | val /= dMultiplier;
392 | if (val < 0)
393 | val = 0;
394 | double rem = val - (int)val;
395 | unsigned long uval = (unsigned long)val;
396 | if (rem >= 0.5)
397 | uval += 1;
398 |
399 | if (uval > 0x7F)
400 | uval = 0x7F;
401 |
402 | c1 = 0;
403 | c2 = (unsigned char)uval;
404 | if (negative)
405 | c2 |= 0x80;
406 | }
407 | }
408 | }
409 | break;
410 | case 6:
411 | {
412 | val = 0xFF;
413 | ok = true;
414 | }
415 | break;
416 | case 7:
417 | {
418 | _TCHAR *end = NULL;
419 | char temps[10];
420 | str_left(temps, str, 8);
421 | val = _tcstod(temps, &end);
422 |
423 | if ((val < 5) || (val > 25.53))
424 | {
425 | ok = false;
426 | }
427 | else
428 | {
429 | c1 = 0;
430 | c2 = ConvertVoltageToChar(val);
431 | ok = true;
432 | }
433 | }
434 | break;
435 | case 8:
436 | {
437 | if (strcmp(str, "Never") == 0)
438 | {
439 | val = (double)0xFFFF * dMultiplier;
440 | ok = true;
441 | }
442 | else
443 | {
444 | TCHAR *end = NULL;
445 | char temps[10];
446 | str_left(temps, str, 8);
447 | val = _tcstod(temps, &end);
448 | if (end != temps)
449 | ok = true;
450 | }
451 | }
452 | break;
453 | case 9:
454 | {
455 | c1 = 0;
456 | c2 = bin2char(str, &ok);
457 | }
458 | break;
459 | case 1:
460 | //normal read - integer
461 | case 2:
462 | case 5:
463 | default:
464 | { //normal read - float
465 | _TCHAR *end = NULL;
466 | char temps[10];
467 | str_left(temps, str, 8);
468 | val = _tcstod(temps, &end);
469 |
470 | // CString err;
471 | // err.Format("Convert: (%f) (%d) (%s)",val, errno, str.Left(8));
472 | // AfxMessageBox(err);
473 |
474 | if (end != temps)
475 | ok = true;
476 | }
477 | }
478 |
479 | if (ok)
480 | {
481 | if ((nReadMode != 4) && (nReadMode != 7) && (nReadMode != 9))
482 | {
483 | val /= dMultiplier;
484 |
485 | if (nReadMode != 5)
486 | { //unsigned
487 | if (val < 0)
488 | val = 0;
489 | double rem = val - (int)val;
490 | unsigned long uval = (unsigned long)val;
491 | if (rem >= 0.5)
492 | uval += 1;
493 |
494 | if (len == 1)
495 | {
496 | if (uval > 0xFF)
497 | uval = 0xFF;
498 |
499 | c1 = 0;
500 | c2 = (unsigned char)uval;
501 | }
502 | else
503 | {
504 | if (uval > 0xFFFF)
505 | uval = 0xFFFF;
506 |
507 | c1 = (unsigned char)((((unsigned int)uval) >> 8) & 0xFF);
508 | c2 = (unsigned char)(((unsigned int)uval) & 0xFF);
509 | }
510 | }
511 | else
512 | {
513 | double rem = -val + (int)val;
514 | long uval = (long)val;
515 | if (rem >= 0.5)
516 | uval += 1;
517 |
518 | if (len == 1)
519 | {
520 | if (uval > 127)
521 | uval = 127;
522 | if (uval < -128)
523 | uval = -128;
524 |
525 | c1 = 0;
526 | c2 = (char)uval;
527 | }
528 | else
529 | {
530 | if (uval > 32767)
531 | uval = 32767;
532 | if (uval < -32768)
533 | uval = -32768;
534 |
535 | c1 = (char)((((int)uval) >> 8) & 0xFF);
536 | c2 = (char)(((int)uval) & 0xFF);
537 | }
538 | }
539 | }
540 | }
541 |
542 | //fprintf(stderr, "readOneValue: readMode: %d c1: 0x%02x c2: 0x%02x\n", nReadMode, c1, c2);
543 |
544 | return ok;
545 | }
546 |
547 | void HIDDCDCUSB::convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier)
548 | {
549 | destination[0] = 0; //empty string
550 | double value = 0;
551 |
552 | if (nLen == 1)
553 | {
554 | value = (double)m_chPackages[nIndex] * dMultiplier;
555 | }
556 | else
557 | {
558 | value = (double)((((int)m_chPackages[nIndex]) << 8) | m_chPackages[nIndex + 1]) * dMultiplier;
559 | }
560 |
561 | switch (nReadMode)
562 | {
563 | case 2:
564 | sprintf(destination, "%.2f", (float)value);
565 | break;
566 | case 3:
567 | {
568 | unsigned int ivalue = (unsigned int)value;
569 | if (ivalue == 0xFFFF)
570 | strcpy(destination, "Never");
571 | else
572 | sprintf(destination, "%02d:%02d:%02d", ivalue / 3600, (ivalue / 60) % 60, ivalue % 60);
573 | }
574 | break;
575 | case 4:
576 | {
577 | double dvalue = (m_chPackages[nIndex] & 0x7f) * dMultiplier;
578 |
579 | if ((m_chPackages[nIndex] & 0x80) != 0)
580 | sprintf(destination, "-%.3f", (float)dvalue);
581 | else
582 | sprintf(destination, "%.3f", (float)value);
583 | }
584 | break;
585 | case 5:
586 | {
587 | if (nLen == 1)
588 | value = (double)((char)m_chPackages[nIndex]) * dMultiplier;
589 | else
590 | value = (double)((((int)((char)m_chPackages[nIndex])) << 8) | m_chPackages[nIndex + 1]) * dMultiplier;
591 |
592 | sprintf(destination, "%d", (int)value);
593 | }
594 | break;
595 | case 6:
596 | {
597 | strcpy(destination, "-");
598 | }
599 | break;
600 | case 7:
601 | { //no multiplier - voltage value calcualted with formula
602 | sprintf(destination, "%.3f", HIDDCDCUSB::GetVOut(m_chPackages[nIndex]));
603 | }
604 | break;
605 | case 8:
606 | {
607 | if (nLen == 1)
608 | {
609 | if (m_chPackages[nIndex] == 0xFF)
610 | strcpy(destination, "Never");
611 | else
612 | sprintf(destination, "%d", (int)value);
613 | }
614 | else
615 | {
616 | if ((m_chPackages[nIndex] == 0xFF) && (m_chPackages[nIndex + 1] == 0xFF))
617 | strcpy(destination, "Never");
618 | else
619 | sprintf(destination, "%d", (int)value);
620 | }
621 | }
622 | break;
623 | case 9:
624 | char2bin(destination, m_chPackages[nIndex]);
625 | break;
626 | case 1:
627 | default:
628 | {
629 | sprintf(destination, "%d", (int)value);
630 | }
631 | }
632 |
633 | //DBGOutput("convertOneValue2String %d %s %f [%x][%x]\n",nIndex,destination,(float)value,(int)m_chPackages[nIndex],(int)m_chPackages[nIndex+1]);
634 | }
635 |
636 | bool HIDDCDCUSB::setVariableData(int mesg_no, char *str)
637 | {
638 | int len = GetMessages()[mesg_no].nLen;
639 |
640 | if (len != 0)
641 | {
642 | unsigned char c1 = 0;
643 | unsigned char c2 = 0;
644 | unsigned char c3 = 0;
645 | unsigned char c4 = 0;
646 |
647 | if (readOneValue(str, GetMessages()[mesg_no].nReadMode, GetMessages()[mesg_no].dMultiplier, len, c1, c2, c3, c4))
648 | {
649 | switch (len)
650 | {
651 | case 1:
652 | m_chPackages[GetMessages()[mesg_no].nIndex] = c2;
653 | break;
654 | case 2:
655 | m_chPackages[GetMessages()[mesg_no].nIndex] = c1;
656 | m_chPackages[GetMessages()[mesg_no].nIndex + 1] = c2;
657 | break;
658 | case 4:
659 | m_chPackages[GetMessages()[mesg_no].nIndex] = c1;
660 | m_chPackages[GetMessages()[mesg_no].nIndex + 1] = c2;
661 | m_chPackages[GetMessages()[mesg_no].nIndex + 2] = c3;
662 | m_chPackages[GetMessages()[mesg_no].nIndex + 3] = c4;
663 | break;
664 | }
665 | return 1;
666 | }
667 | else
668 | return 0;
669 | }
670 | return 1;
671 | }
672 |
673 | unsigned char HIDDCDCUSB::getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment)
674 | {
675 | if ((cnt < 0) || (cnt >= DCDCUSB_MAX_MESSAGE_CNT))
676 | return 0;
677 |
678 | if (HIDDCDCUSB::GetMessages()[cnt].nLen == 0)
679 | return 0;
680 |
681 | if (name)
682 | strcpy(name, HIDDCDCUSB::GetMessages()[cnt].strName);
683 | if (unit)
684 | strcpy(unit, HIDDCDCUSB::GetMessages()[cnt].strUnit);
685 | if (comment)
686 | strcpy(comment, HIDDCDCUSB::GetMessages()[cnt].strText);
687 |
688 | if (value)
689 | {
690 | convertOneValue2String(value, HIDDCDCUSB::GetMessages()[cnt].nLen,
691 | HIDDCDCUSB::GetMessages()[cnt].nIndex,
692 | HIDDCDCUSB::GetMessages()[cnt].nReadMode,
693 | HIDDCDCUSB::GetMessages()[cnt].dMultiplier);
694 | }
695 |
696 | return HIDDCDCUSB::GetMessages()[cnt].nReadMode;
697 | }
698 |
699 | void HIDDCDCUSB::restartUPS()
700 | {
701 | }
702 |
703 | void HIDDCDCUSB::restartUPSInBootloaderMode()
704 | {
705 | }
706 |
707 | void HIDDCDCUSB::setVOutVolatile(char *param) {
708 |
709 | if (!param)
710 | return;
711 |
712 | double voltage;
713 | char *end, temps[10];
714 | unsigned char val;
715 |
716 | voltage = strtod(param, &end);
717 | fprintf(stderr, "Setting VOut voltage to: %lf\n", voltage);
718 | val = ConvertVoltageToChar(voltage);
719 |
720 | sendMessage(DCDCUSB_CMD_OUT, 3, DCDCUSB_CMD_WRITE_VOUT, val, 0);
721 | }
722 |
723 | void HIDDCDCUSB::setVOutVolatile(float vout)
724 | {
725 | unsigned char val = ConvertVoltageToChar(vout);
726 | sendMessage(DCDCUSB_CMD_OUT, 3, DCDCUSB_CMD_WRITE_VOUT, val, 0);
727 | }
728 |
729 | void HIDDCDCUSB::incDecVOutVolatile(unsigned char inc)
730 | {
731 | }
732 |
733 | void HIDDCDCUSB::GetStatus()
734 | {
735 | unsigned char recv[32];
736 | int ret;
737 |
738 | sendMessage(DCDCUSB_GET_ALL_VALUES, 0);
739 | recvMessage(recv);
740 | parseMessage(recv);
741 |
742 | sendMessage(DCDCUSB_CMD_OUT, 3, DCDCUSB_CMD_READ_VOUT, 0, 0);
743 | recvMessage(recv);
744 | parseMessage(recv);
745 | }
746 |
747 | void HIDDCDCUSB::ReadConfigurationMemory()
748 | {
749 | unsigned char recv[32];
750 |
751 | m_ulSettingsAddr = DCDCUSB_SETTINGS_ADDR_START;
752 | memset(m_chPackages, 0, DCDCUSB_SETTINGS_PACKS * 16);
753 |
754 | while (m_ulSettingsAddr < DCDCUSB_SETTINGS_ADDR_END)
755 | {
756 | int ret = sendMessage(DCDCUSB_MEM_READ_OUT, 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x10);
757 | recvMessage(recv);
758 | parseMessage(recv);
759 | m_ulSettingsAddr += 16;
760 | }
761 | }
762 |
763 | void HIDDCDCUSB::EraseConfigurationMemory()
764 | {
765 | unsigned char recv[32];
766 | int ret;
767 |
768 | m_ulSettingsAddr = DCDCUSB_SETTINGS_ADDR_START;
769 | sendMessage(DCDCUSB_MEM_ERASE, 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x40);
770 |
771 | int retries = 5;
772 | while ((ret = recvMessage(recv) <= 0) && retries > 0)
773 | {
774 | retries--;
775 | usleep(500);
776 | fprintf(stderr, "Erase 0x%02x retry %d/5\n", recv[0], retries);
777 | }
778 |
779 | if (retries <= 0)
780 | {
781 | fprintf(stderr, "Error waiting for erase operation to finish\n");
782 | }
783 | else
784 | {
785 | fprintf(stderr, "Successfully erased (0x%02x) configuration memory\n", recv[0]);
786 | }
787 | }
788 |
789 | void HIDDCDCUSB::WriteConfigurationMemory()
790 | {
791 | unsigned char recv[32];
792 | int ret;
793 |
794 | m_ulSettingsAddr = DCDCUSB_SETTINGS_ADDR_START;
795 | while (m_ulSettingsAddr < DCDCUSB_SETTINGS_ADDR_END)
796 | {
797 | sendMessageWithBuffer(DCDCUSB_MEM_WRITE_OUT, 16, m_chPackages + (m_ulSettingsAddr - DCDCUSB_SETTINGS_ADDR_START), 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x10);
798 | ret = recvMessage(recv);
799 |
800 | if (ret <= 0)
801 | {
802 | fprintf(stderr, "Error (%d, 0x%02x) writing configuration variables , aborting ...\n", ret, recv[0]);
803 | break;
804 | }
805 | else
806 | {
807 | //fprintf(stderr, "Wrote page 0x%lx of configuration\n", m_ulSettingsAddr);
808 | }
809 | m_ulSettingsAddr += 16;
810 | }
811 | }
812 |
813 | bool HIDDCDCUSB::executeCommand(char *cmdexpr) {
814 | fprintf(stdout, "Executing command and params: %s\n", cmdexpr);
815 | struct EXEC *exec = parseCommand(cmdexpr);
816 | bool executed = false;
817 |
818 | if ((strcmp(exec->cmd, "set_vout") == 0) && (exec->params_count > 0)) {
819 | setVOutVolatile(exec->params[0]);
820 | executed = true;
821 | } else {
822 | fprintf(stderr, "Unknown command: %s\n", exec->cmd);
823 | }
824 |
825 | free(exec);
826 | return executed;
827 | }
--------------------------------------------------------------------------------
/src/lib/HIDDCDCUSB.h:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ifndef _HIDDCDCUSB_H_
4 | #define _HIDDCDCUSB_H_
5 |
6 | #include "HIDInterface.h"
7 | #include "usbhid.h"
8 |
9 | #define MAX_BUF 500
10 |
11 | class HIDGraphics;
12 |
13 | #define STATUS_OK 0x00
14 | #define STATUS_ERASE 0x01
15 | #define STATUS_WRITE 0x02
16 | #define STATUS_READ 0x03
17 | #define STATUS_ERROR 0xff
18 |
19 | #define DCDCUSB_SETTINGS_ADDR_START 0x003FC0
20 | #define DCDCUSB_SETTINGS_ADDR_END 0x004000
21 | #define DCDCUSB_SETTINGS_PACKS 4
22 |
23 | #define DCDCUSB_GET_ALL_VALUES 0x81
24 | #define DCDCUSB_RECV_ALL_VALUES 0x82
25 | #define DCDCUSB_CMD_OUT 0xB1
26 | #define DCDCUSB_CMD_IN 0xB2
27 | #define DCDCUSB_MEM_READ_OUT 0xA1
28 | #define DCDCUSB_MEM_READ_IN 0xA2
29 | #define DCDCUSB_MEM_WRITE_OUT 0xA3
30 | #define DCDCUSB_MEM_WRITE_IN 0xA4
31 | #define DCDCUSB_MEM_ERASE 0xA5
32 |
33 | #define DCDCUSB_INTERNAL_MESG 0xFF
34 | #define DCDCUSB_INTERNAL_MESG_DISCONNECTED 0x01
35 |
36 | #define DCDCUSB_CMD_SET_AUX_WIN 0x01
37 | #define DCDCUSB_CMD_SET_PW_SWITCH 0x02
38 | #define DCDCUSB_CMD_SET_OUTPUT 0x03
39 | #define DCDCUSB_CMD_WRITE_VOUT 0x06
40 | #define DCDCUSB_CMD_READ_VOUT 0x07
41 | #define DCDCUSB_CMD_INC_VOUT 0x0C
42 | #define DCDCUSB_CMD_DEC_VOUT 0x0D
43 | #define DCDCUSB_CMD_LOAD_DEFAULTS 0x0E
44 | #define DCDCUSB_CMD_SCRIPT_START 0x10
45 | #define DCDCUSB_CMD_SCRIPT_STOP 0x11
46 | #define DCDCUSB_CMD_SLEEP 0x12
47 | #define DCDCUSB_CMD_READ_REGULATOR_STEP 0x13
48 |
49 | //For reading out memory
50 | #define DCDCUSB_TYPE_CODE_MEMORY 0x00
51 | #define DCDCUSB_TYPE_EPROM_EXTERNAL 0x01
52 | #define DCDCUSB_TYPE_EPROM_INTERNAL 0x02
53 | #define DCDCUSB_TYPE_CODE_SPLASH 0x03
54 |
55 | #define DCDCUSB_FLASH_REPORT_ERASE_MEMORY 0xF2 // AddressLo : AddressHi : AddressUp (anywhere inside the 64 byte-block to be erased)
56 | #define DCDCUSB_FLASH_REPORT_READ_MEMORY 0xF3 // AddressLo : AddressHi : AddressUp : Data Length (1...32)
57 | #define DCDCUSB_FLASH_REPORT_WRITE_MEMORY 0xF4 // AddressLo : AddressHi : AddressUp : Data Length (1...32) : Data....
58 | #define DCDCUSB_KEYBD_REPORT_ERASE_MEMORY 0xB2 // same as F2 but in keyboard mode
59 | #define DCDCUSB_KEYBD_REPORT_READ_MEMORY 0xB3 // same as F3 but in keyboard mode
60 | #define DCDCUSB_KEYBD_REPORT_WRITE_MEMORY 0xB4 // same as F4 but in keyboard mode
61 | #define DCDCUSB_KEYBD_REPORT_MEMORY 0x41 // response to b3,b4
62 |
63 | #define IN_REPORT_EXT_EE_DATA 0x31
64 | #define OUT_REPORT_EXT_EE_READ 0xA1
65 | #define OUT_REPORT_EXT_EE_WRITE 0xA2
66 |
67 | #define IN_REPORT_INT_EE_DATA 0x32
68 | #define OUT_REPORT_INT_EE_READ 0xA3
69 | #define OUT_REPORT_INT_EE_WRITE 0xA4
70 |
71 | ///// MEASUREMENT CONSTANTS
72 | #define DCDCUSB_CT_RW (double)75
73 | #define DCDCUSB_CT_R1 (double)49900
74 | #define DCDCUSB_CT_R2 (double)1500
75 | #define DCDCUSB_CT_RP (double)10000
76 |
77 | #define CHECK_CHAR (unsigned char)0xAA //used for line/write check
78 |
79 | #define DCDCUSB_MAX_MESSAGE_CNT 64
80 |
81 | /////
82 | class HIDDCDCUSB : public HIDInterface
83 | {
84 |
85 | public:
86 | HIDDCDCUSB(USBHID *d);
87 | virtual ~HIDDCDCUSB();
88 |
89 | void parseMessage(unsigned char *msg);
90 | void printValues();
91 | void printConfiguration();
92 | bool executeCommand(char *cmdexpr);
93 |
94 | bool readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4);
95 | float convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier);
96 | void convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier);
97 | bool setVariableData(int mesg_no, char *str);
98 | ATXMSG *GetMessages();
99 | double GetConstant(int i);
100 | unsigned int *GetTermistorConsts();
101 | int GetMessageIdxByName(const char *name);
102 | unsigned char getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment);
103 | void restartUPS();
104 | void restartUPSInBootloaderMode();
105 |
106 | void GetStatus();
107 | void ReadConfigurationMemory();
108 | void EraseConfigurationMemory();
109 | void WriteConfigurationMemory();
110 |
111 |
112 |
113 | private:
114 | int m_nVoltageCfg;
115 | float m_fVIn;
116 | float m_fIgn;
117 | float m_fVOut;
118 | float m_fVOutSet;
119 | bool m_bPowerSwitch;
120 | bool m_bOutput;
121 | bool m_bAuxVin;
122 | bool m_bVersion;
123 |
124 | int m_nScriptPointer;
125 | unsigned char m_nVerMajor;
126 | unsigned char m_nVerMinor;
127 | int m_nMode;
128 | int m_nState;
129 | int m_nTimeCfg;
130 |
131 | char m_strFlagsVoltage[256];
132 | char m_strFlagsStatus1[256];
133 | char m_strFlagsStatus2[256];
134 | char m_strFlagsTimer[256];
135 | char m_strVoltCfg[256];
136 | char m_strMode[256];
137 | char m_strTimerWait[256];
138 | char m_strTimerVout[256];
139 | char m_strTimerPwSwitch[256];
140 | char m_strFlashPointer[256];
141 | char m_strTimerVAux[256];
142 | char m_strTimerOffDelay[256];
143 | char m_strTimerHardOff[256];
144 | char m_editRegulatorSteps[256];
145 |
146 | double GetVOut(unsigned char data);
147 | unsigned char ConvertVoltageToChar(double vout);
148 | void setVOutVolatile(char *param);
149 | void setVOutVolatile(float vout);
150 | void incDecVOutVolatile(unsigned char inc);
151 |
152 | };
153 |
154 | #endif
155 |
--------------------------------------------------------------------------------
/src/lib/HIDInterface.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "HIDInterface.h"
15 | #include "HArray.h"
16 |
17 | #define OPENUPS2_CMD_OUT 0xB1
18 |
19 |
20 | HIDInterface::HIDInterface(USBHID *hidDevice) {
21 | d = hidDevice;
22 | memset(m_chPackages, 0, SETTINGS_PACKS * 16);
23 | }
24 |
25 | HIDInterface::~HIDInterface() {
26 |
27 | }
28 |
29 | void HIDInterface::printConfiguration(bool withComments)
30 | {
31 | int var_max = 256;
32 | char name[256];
33 | char unit[64];
34 | char value[256];
35 | char comment[1024];
36 |
37 | for (int i = 0; i < var_max; i++)
38 | {
39 | if (getUPSVariableData(i, name, value, unit, comment))
40 | {
41 | if (withComments) {
42 | fprintf(stdout, "%d. %s: %s # %s %s\n", i, name, value, unit, comment);
43 | fprintf(stdout, "%s\n", "---------------------------------------------------------------------------------------------------------------");
44 | } else {
45 | fprintf(stdout, "%s=%s\n", name, value);
46 | }
47 | }
48 | }
49 | }
50 |
51 | int HIDInterface::sendMessageWithBuffer(unsigned char cType, unsigned int buflen, unsigned char* buffer, unsigned int len, ...)
52 | {
53 | HArray mesg(buflen + len + 1);
54 | mesg.getBuf()[0] = cType;
55 |
56 | va_list args;
57 | va_start(args, len);
58 | unsigned int cnt = 0;
59 | unsigned char i = va_arg(args, unsigned int);
60 |
61 | while (cnt < len)
62 | {
63 | mesg.getBuf()[1+cnt] = i;
64 | cnt++;
65 | if (cnt < len)
66 | i = va_arg(args, unsigned int);
67 | }
68 |
69 | va_end(args);
70 |
71 | memcpy(mesg.getBuf() + 1 + len, buffer, buflen);
72 |
73 | int ret = d->writeInterrupt(mesg.getBuf(), mesg.length(), true);
74 | //fprintf(stderr, "[0x%02x] written %d/%d\n", cType, ret, len + 1);
75 | return ret;
76 | }
77 |
78 | int HIDInterface::sendMessage(unsigned char cType, unsigned int len, ...)
79 | {
80 | HArray mesg(len + 1);
81 | //HArray mesg(24);
82 | mesg.getBuf()[0] = cType;
83 |
84 | va_list args;
85 | va_start(args, len);
86 | unsigned int cnt = 0;
87 |
88 | unsigned char i = va_arg(args, unsigned int);
89 | while (cnt < len)
90 | {
91 | mesg.getBuf()[1 + cnt] = i;
92 | cnt++;
93 | if (cnt < len)
94 | i = va_arg(args, unsigned int);
95 | }
96 | va_end(args);
97 |
98 | int ret = d->writeInterrupt(mesg.getBuf(), mesg.length(), true);
99 | //fprintf(stderr, "[0x%02x] written %d/%d\n", cType, ret, len + 1);
100 | return ret;
101 | }
102 |
103 | int HIDInterface::sendCommand(unsigned char command, unsigned char value)
104 | {
105 | return sendMessage(COMMON_CMD_OUT, 3, command, value, 0); // OpenUPS, OpenUPS2 and NUCUPS share same CMD_OUT value
106 | }
107 | int HIDInterface::sendCommandEx(unsigned char command, unsigned char value1, unsigned char value2)
108 | {
109 | return sendMessage(COMMON_CMD_OUT, 3, command, value1, value2);
110 | }
111 |
112 | int HIDInterface::recvMessage(unsigned char *buffer)
113 | {
114 | int ret = d->readInterrupt(buffer);
115 |
116 | if (ret <= 0 ) {
117 | fprintf(stderr, "Read error [0x%02x]: %d\n", buffer[0], ret);
118 | }
119 | return ret;
120 | }
121 |
122 | int HIDInterface::recvMessage(unsigned char *buffer, unsigned int len)
123 | {
124 | int ret = d->readInterrupt(buffer, len);
125 |
126 | if (ret <= 0 ) {
127 | fprintf(stderr, "Read error [0x%02x]: %d/%d\n", buffer[0], ret, len);
128 | }
129 | return ret;
130 | }
131 |
132 | int HIDInterface::GetMessageIdxByName(const char* name)
133 | {
134 | if (!name)
135 | return -1;
136 |
137 | for (int i = 0;i < MAX_MESSAGE_CNT; i++)
138 | {
139 | ATXMSG msg = GetMessages()[i];
140 | //fprintf(stderr, "GetMessage %d: %s nLen: %d cmp: %d\n", i, msg.strName, msg.nLen, strcasecmp(msg.strName, name));
141 | if (msg.nLen != 0)
142 | if (strcasecmp(msg.strName, name) == 0)
143 | return i;
144 | }
145 |
146 | return -1;
147 | }
148 |
149 | int HIDInterface::varsToFile(const char *filename, bool withComments) {
150 | if (!filename) {
151 | return -1;
152 | }
153 |
154 | FILE *f = fopen(filename, "w");
155 |
156 | if (!f) {
157 | return -2;
158 | }
159 |
160 | int var_max = 256;
161 | char name[256];
162 | char unit[64];
163 | char value[256];
164 | char comment[1024];
165 |
166 | for (int i = 0; i < var_max; i++)
167 | {
168 | if (getUPSVariableData(i, name, value, unit, comment))
169 | {
170 | if (withComments) {
171 | // Replace \r \n with comment #, assumes windows style line break
172 | char *p = comment;
173 | while (p && *p != '\0') {
174 | if (*p == '\r') {
175 | if (*(p + 1) == '\n') {
176 | *p = '\n';
177 | *(p + 1) = '#';
178 | }
179 | }
180 | p++;
181 | }
182 | fprintf(f, "# %s %s %s\n\n", name, unit, comment);
183 | }
184 | fprintf(f, "%s=%s\n", name, value);
185 | }
186 | }
187 | fclose(f);
188 | return 0;
189 | }
190 |
191 | int HIDInterface::fileToVars(const char *filename) {
192 | if (!filename) {
193 | return -1;
194 | }
195 |
196 | FILE *f = fopen(filename, "r");
197 |
198 | if (!f) {
199 | fprintf(stderr, "Failed to open %s for writting\n", filename);
200 | return -2;
201 | }
202 |
203 | int ret;
204 | char name[256];
205 | char val[256];
206 | char buf[1024];
207 |
208 | #define MAX_LINE 1024
209 | char line[MAX_LINE];
210 | int len;
211 | int lineno = 0, last = 0, vars = 0;
212 |
213 | while (fgets(line, MAX_LINE, f) != NULL) {
214 | len = strlen(line) - 1;
215 | if (line[len] != '\n' && !feof(f)) {
216 | fprintf(stderr, "Line %d too long\n", lineno);
217 | fclose(f);
218 | return -3;
219 | }
220 | lineno++;
221 |
222 | /* Remove \n and spaces from the end */
223 | while ((len >= 0) && ((line[len]=='\n') || (isspace(line[len])))) {
224 | line[len] = 0 ;
225 | len--;
226 | }
227 |
228 | if (len < 0) {
229 | len = 0;
230 | continue;
231 | }
232 | if (sscanf(line, "%32[^=]=%32[^\n]%*c", name, val) == 2){
233 | //fprintf(stdout, "Found var: %s value: %s\n", name, val);
234 | int idx;
235 | idx = GetMessageIdxByName(name);
236 | if (idx >= 0) {
237 | if (setVariableData(idx, val)) {
238 | //fprintf(stdout, "Succesfully converted %s to %s\n", name, val);
239 | } else {
240 | fprintf(stderr, "Error converting %s to %s\n", name, val);
241 | }
242 | } else {
243 | fprintf(stderr, "Variable '%s' unknown\n", name);
244 | }
245 | vars++;
246 | }
247 | }
248 | fprintf(stdout, "Found %d vars\n", vars);
249 | fclose(f);
250 |
251 | return 0;
252 | }
253 |
254 | struct EXEC* HIDInterface::parseCommand(char *cmdexpr) {
255 | if (!cmdexpr)
256 | return NULL;
257 |
258 | struct EXEC *exec = (struct EXEC *) malloc (sizeof (struct EXEC));
259 | unsigned int idx = 0;
260 | char *token;
261 |
262 | while ((token = strsep(&cmdexpr, CMD_DELIM))) {
263 | if (idx == 0) {
264 | strncpy(exec->cmd, token, MAX_CMD_LEN);
265 | exec->cmd[MAX_CMD_LEN] = '\0';
266 | fprintf(stdout, "Found command: %s\n", exec->cmd);
267 | } else {
268 | strncpy(exec->params[idx - 1], token, MAX_PARAM_LEN);
269 | exec->params[idx - 1][MAX_PARAM_LEN] = '\0';
270 | fprintf(stdout, "Found param %d: %s\n", idx, exec->params[idx - 1]);
271 | }
272 | idx++;
273 | }
274 | exec->params_count = idx;
275 | return exec;
276 | }
277 |
278 | bool HIDInterface::executeCommand(char *cmdexpr) {
279 | fprintf(stderr, "Commands not implemented !\n");
280 | return false;
281 | }
--------------------------------------------------------------------------------
/src/lib/HIDInterface.h:
--------------------------------------------------------------------------------
1 | #ifndef _HIDINTERFACE_H_
2 | #define _HIDINTERFACE_H_
3 |
4 | #include "usbhid.h"
5 |
6 | #define COMMON_CMD_OUT 0xB1
7 | #define COMMON_CMD_IN 0xB2
8 | #define SETTINGS_PACKS 64
9 | #define DBG_LEN 25 //4 + 17 + 4
10 | #define MAX_MESSAGE_CNT 256
11 | #define MAX_CMD_LEN 32
12 | #define MAX_PARAM_LEN 32
13 | #define MAX_CMD_PARAMS 16
14 | #define CMD_DELIM ":"
15 |
16 | struct ATXMSG
17 | {
18 | unsigned int nIndex;
19 | unsigned int nLen;
20 | const char* strName;
21 | bool bEnabled; // enabled to write or just to read
22 | int nReadMode; // 1=normal, 2=float, 3=hh:mm:ss
23 | double dMultiplier;
24 | double dLimitMin;
25 | double dLimitMax;
26 | const char* strText; // description/comment
27 | const char* strUnit; // measurement unit
28 | };
29 | typedef ATXMSG _ATXMSG;
30 |
31 | struct UVP
32 | {
33 | unsigned char nCVR_1;
34 | unsigned char nCVR_2;
35 | double fVoltage;
36 | };
37 |
38 | struct EXEC
39 | {
40 | char cmd[MAX_CMD_LEN + 1];
41 | char params[MAX_CMD_PARAMS][MAX_PARAM_LEN + 1];
42 | unsigned int params_count;
43 | };
44 |
45 | class HIDInterface {
46 | public:
47 | HIDInterface(USBHID *d);
48 | virtual ~HIDInterface();
49 |
50 | virtual void GetStatus() = 0;
51 | virtual void ReadConfigurationMemory() = 0;
52 | virtual void EraseConfigurationMemory() = 0;
53 | virtual void WriteConfigurationMemory() = 0;
54 |
55 | int sendMessageWithBuffer(unsigned char cType, unsigned int buflen, unsigned char* buffer, unsigned int len, ...);
56 | int sendMessage(unsigned char cType, unsigned int len, ...);
57 | int sendCommand(unsigned char command, unsigned char value);
58 | int sendCommandEx(unsigned char command, unsigned char value1, unsigned char value2);
59 | int recvMessage(unsigned char *buffer);
60 | int recvMessage(unsigned char *buffer, unsigned int len);
61 | int GetMessageIdxByName(const char* name);
62 | int varsToFile(const char *filename, bool withComments);
63 | int fileToVars(const char *filename);
64 | void printConfiguration(bool withComments);
65 | struct EXEC* parseCommand(char *cmdexpr);
66 |
67 | virtual bool executeCommand(char *cmdexpr);
68 | virtual void parseMessage(unsigned char *msg) = 0;
69 | virtual void printValues() = 0;
70 | virtual float convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier) = 0;
71 | virtual bool readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4) = 0;
72 | virtual void convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier) = 0;
73 | virtual bool setVariableData(int mesg_no, char *str) = 0;
74 | virtual ATXMSG* GetMessages() = 0;
75 | virtual double GetConstant(int i) = 0;
76 | virtual unsigned int* GetTermistorConsts() = 0;
77 | virtual unsigned char getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment) = 0;
78 | virtual void restartUPS() = 0;
79 | virtual void restartUPSInBootloaderMode() = 0;
80 |
81 | unsigned long m_ulSettingsAddr;
82 | unsigned char m_chPackages[SETTINGS_PACKS * 16];
83 |
84 | USBHID *d;
85 | };
86 |
87 | #endif // _HIDINTERFACE_H_
--------------------------------------------------------------------------------
/src/lib/HIDNUCUPS.h:
--------------------------------------------------------------------------------
1 | #ifndef _HIDNUCUPS_H_
2 | #define _HIDINUCUPS_H_
3 |
4 | #include "usbhid.h"
5 | #include "util.h"
6 | #include "HIDInterface.h"
7 |
8 | #define NUC_OUT_REPORT_IO_DATA 0x81
9 | #define NUC_IN_REPORT_IO_DATA 0x82
10 | #define NUC_OUT_REPORT_IO_DATA2 0x83
11 | #define NUC_IN_REPORT_IO_DATA2 0x84
12 | #define NUC_OUT_REPORT_IO_DATA3 0x85
13 | #define NUC_IN_REPORT_IO_DATA3 0x86
14 | #define NUC_OUT_REPORT_IO_DATA4 0x87
15 | #define NUC_IN_REPORT_IO_DATA4 0x88
16 | #define NUC_OUT_REPORT_IO_DATA5 0x89
17 | #define NUC_IN_REPORT_IO_DATA5 0x8A
18 |
19 | #define NUC_CMD_OUT 0xB1
20 | #define NUC_CMD_IN 0xB2
21 | #define NUC_MEM_READ_OUT 0xA1
22 | #define NUC_MEM_READ_IN 0xA2
23 | #define NUC_MEM_WRITE_OUT 0xA3
24 | #define NUC_MEM_WRITE_IN 0xA4
25 | #define NUC_MEM_ERASE 0xA5
26 |
27 | #define NUC_ENTER_BOOTLOADER_OUT 0xA9
28 | #define NUC_ENTER_BOOTLOADER_IN 0xAA
29 |
30 | #define INTERNAL_MESG 0xFF
31 | #define INTERNAL_MESG_DISCONNECTED 0x01
32 |
33 | //commands
34 | #define DCMD_RESTART_NUC 0xAA
35 |
36 | //For reading out memory
37 | #define TYPE_CODE_MEMORY 0x00
38 | #define TYPE_EPROM_EXTERNAL 0x01
39 | #define TYPE_EPROM_INTERNAL 0x02
40 | #define TYPE_CODE_SPLASH 0x03
41 |
42 | #define FLASH_REPORT_ERASE_MEMORY 0xF2 // AddressLo : AddressHi : AddressUp (anywhere inside the 64 byte-block to be erased)
43 | #define FLASH_REPORT_READ_MEMORY 0xF3 // AddressLo : AddressHi : AddressUp : Data Length (1...32)
44 | #define FLASH_REPORT_WRITE_MEMORY 0xF4 // AddressLo : AddressHi : AddressUp : Data Length (1...32) : Data....
45 | #define KEYBD_REPORT_ERASE_MEMORY 0xB2 // same as F2 but in keyboard mode
46 | #define KEYBD_REPORT_READ_MEMORY 0xB3 // same as F3 but in keyboard mode
47 | #define KEYBD_REPORT_WRITE_MEMORY 0xB4 // same as F4 but in keyboard mode
48 | #define KEYBD_REPORT_MEMORY 0x41 // response to b3,b4
49 |
50 | #define IN_REPORT_EXT_EE_DATA 0x31
51 | #define OUT_REPORT_EXT_EE_READ 0xA1
52 | #define OUT_REPORT_EXT_EE_WRITE 0xA2
53 |
54 | #define IN_REPORT_INT_EE_DATA 0x32
55 | #define OUT_REPORT_INT_EE_READ 0xA3
56 | #define OUT_REPORT_INT_EE_WRITE 0xA4
57 |
58 | ///// MEASUREMENT CONSTANTS
59 | #define NUC_CT_RW (double)75
60 | #define NUC_CT_RP (double)5000
61 | #define NUC_CT_R1_VOUT (double)56200
62 | #define NUC_CT_R2_VOUT (double)2740
63 | #define NUC_CT_V_FEEDBACK_OUT (double)1.2
64 |
65 | #define CHECK_CHAR (unsigned char)0xAA //used for line/write check
66 |
67 | #define MAX_MESSAGE_CNT 256
68 |
69 | #define TERMISTOR_CONSTS 34
70 |
71 | #define NUC_SETTINGS_ADDR_START 0x003000
72 | #define NUC_SETTINGS_ADDR_END 0x003100
73 | #define NUC_SETTINGS_PACKS 16
74 |
75 |
76 | class HIDNUCUPS: public HIDInterface {
77 | public:
78 | HIDNUCUPS(USBHID *d);
79 | ~HIDNUCUPS();
80 |
81 | void GetStatus();
82 | void ReadConfigurationMemory();
83 | void EraseConfigurationMemory();
84 | void WriteConfigurationMemory();
85 | void parseMessage(unsigned char *msg);
86 | void printValues();
87 | float convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier);
88 | bool readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4);
89 | bool readOneValue(int mesg_no, char* str, int nReadMode, double dMultiplier, int len, double dMin, double dMax, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4);
90 |
91 | void convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier);
92 | bool setVariableData(int mesg_no, char *str);
93 | ATXMSG* GetMessages();
94 | double GetConstant(int i);
95 | unsigned int* GetTermistorConsts();
96 | unsigned int GetSpecialConstants(unsigned int type, unsigned int cnt);
97 | unsigned char getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment);
98 | void restartUPS();
99 | void restartUPSInBootloaderMode();
100 |
101 | private:
102 | unsigned int m_nInput;
103 | unsigned int m_nOutput;
104 | unsigned int m_nChargerFlags;
105 | unsigned int m_nStateFlags;
106 | unsigned int m_nShutdownFlags;
107 |
108 | float m_fVIn;
109 | float m_fIOut;
110 | float m_fVOut;
111 | float m_fVIgnition;
112 | float m_fPOut;
113 | float m_fTemperature[4];
114 | float m_fBatVoltage[4];
115 | unsigned char m_fCellBalanceOn[4];
116 | unsigned char m_fCellDetected[4];
117 | float m_fBatOverallVoltage;
118 | float m_fBatPack;
119 | float m_fIChgDchg;
120 |
121 | unsigned char m_nVerMajor;
122 | unsigned char m_nVerMinor;
123 |
124 | unsigned char m_bDbgOn;//debug messages should be requested
125 |
126 | unsigned char m_nState;//offline,usb,batpowered,vinpowered
127 | unsigned char m_bDBG[32];
128 | unsigned char m_bDBG2[32];
129 | unsigned char m_bDBG3[32];
130 |
131 | unsigned int m_nTimer[10];
132 | unsigned int m_nStateMachine[5];
133 | unsigned int m_nChgTimer[5];
134 |
135 | unsigned char m_chChargeEndedCondition;
136 | unsigned char m_chBatteryLevel;
137 |
138 | unsigned char m_chMode;
139 |
140 | unsigned int m_nCPUUsage;
141 |
142 | const char *SM_UPS[16] = { "INIT",
143 | "LPWR",
144 | "OFF",
145 | "WAIT_IGNITION_ON_TO_OUTPUT_ON",
146 | "OUTPUT_ON",
147 | "WAIT_OUTPUT_ON_TO_MOBPULSE_ON",
148 | "MOBPULSE_ON",
149 | "ON",
150 | "WAIT_IGNITION_OFF_TO_MOBPULSE_OFF",
151 | "WAIT_HARDOF",
152 | "OUTPUT_OFF",
153 | "BUCKBOOST_ON",
154 | "MOBPULSE_OFF",
155 | "ENTER_LOWPOWER",
156 | "UNKNOWN",
157 | "UNKNOWN"};
158 |
159 |
160 | const char *SM_AFE[8] = { "IDLE",
161 | "INIT",
162 | "GAINSREAD",
163 | "UPDATE",
164 | "NORMAL",
165 | "SHIP01",
166 | "SHIP10",
167 | "SHIPMODE"};
168 |
169 |
170 |
171 | const char *SM_CHG[16] = { "IDLE",
172 | "INIT",
173 | "CONDITION",
174 | "START",
175 | "PRECHARGE_CC",
176 | "WAIT_BULK_CC",
177 | "BULK_CC",
178 | "BULK_CV",
179 | "BULK_END",
180 | "FLOAT",
181 | "UNKNOWN",
182 | "UNKNOWN",
183 | "UNKNOWN",
184 | "UNKNOWN",
185 | "UNKNOWN",
186 | "END"};
187 |
188 |
189 | const char *SM_DTCT[16] = {"IDLE",
190 | "STOP_BALANCE",
191 | "START",
192 | "WMEASCYCLCOMPL",
193 | "START_AGAIN",
194 | "WMEASCYCLCOMPL_AGAIN",
195 | "CALC",
196 | "UNKNOWN",
197 | "UNKNOWN",
198 | "UNKNOWN",
199 | "UNKNOWN",
200 | "UNKNOWN",
201 | "UNKNOWN",
202 | "UNKNOWN",
203 | "UNKNOWN",
204 | "END"};
205 |
206 |
207 |
208 |
209 | const char *SM_LOWP[4] = { "OFF",
210 | "ON",
211 | "STANDBY",
212 | "SHIP"};
213 |
214 |
215 |
216 |
217 |
218 | };
219 |
220 | #endif // _HIDNUCUPS_H_
--------------------------------------------------------------------------------
/src/lib/HIDOpenUPS.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | */
7 |
8 | #include "util.h"
9 | #include "HIDOpenUPS.h"
10 |
11 | #include
12 |
13 | double g_OpenUPS_memConstants[]=
14 | {
15 | 0.03545,//Vin //0
16 | 0.02571,//Vout //1
17 | 0.00857,//Vbat+chg+cell //2
18 | 12.411, //A chg Hi range //3
19 | 0.8274, //A chg Low range //4
20 | 16.113 //A discharge //5
21 | };
22 |
23 | ATXMSG g_OpenUPS_memMessages[MAX_MESSAGE_CNT] =
24 | {
25 | {67, 1, _T("OPENUPSMODE") , true,17, 1, 0, 0 , _T("OpenUPS Mode\r\n0-Auto restart when Input Power is Present=YES\r\n1-Auto restart when Input Power is Present=NO"), _T("[-]") },
26 | {28, 4, _T("CAPACITY") , true,16, 1, 0, 0 , _T("Battery Capacity.\r\nDefault 7000mAh"), _T("[mAh]") },
27 | {70, 1, _T("CELLS") , true, 1, 1, 0, 0 , _T("Number of configured cells to charge/balance\r\nDefault is 1."), _T("[pcs]") },
28 | {80, 2, _T("MOB_ONOFF_TOUT") , true, 1, 10, 0, 0 , _T("The powerswitch on the motherboard(if connected) will be shorted this period to turn the Motherboard ON or OFF\r\nDefault is 500ms"), _T("[mS]") },
29 |
30 | { 0, 1, _T("UPS_CONFIG") , true,14, 1, 0, 0 , _T("Configuration register. Used for enabling disabling modules. LSB bit is b0.\r\nb6- Stop impulse\t\tb5- Start impulse\r\nb4- Coulomb counter\tb3- A/D Low pass Filter module\r\nb2- Balance module\t\tb1- Charge module\r\nb0- Output module\r\nDefault is all enabled"), _T("[bits]") },
31 | {100,1, _T("UPS_CONFIG2") , true,14, 1, 0, 0 , _T("Configuration register (Firmware V1.4+). Used for enabling disabling modules. LSB bit is b0.\r\nbit0: if bit is set and running on battery and measured output power> 8) & 0xFF, 0x00, 0x10);
233 | recvMessage(recv);
234 | parseMessage(recv);
235 | m_ulSettingsAddr += 16;
236 | }
237 | }
238 |
239 | void HIDOpenUPS::EraseConfigurationMemory()
240 | {
241 | unsigned char recv[32];
242 | int ret;
243 |
244 | m_ulSettingsAddr = OPENUPS_SETTINGS_ADDR_START;
245 | sendMessage(OPENUPS_MEM_ERASE, 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x40);
246 |
247 | int retries = 5;
248 | while ((ret = recvMessage(recv) <= 0) && retries > 0) {
249 | retries--;
250 | usleep(500);
251 | fprintf(stderr, "Erase 0x%02x retry %d/5\n", recv[0], retries);
252 | }
253 |
254 | if (retries <= 0) {
255 | fprintf(stderr, "Error waiting for erase operation to finish\n");
256 | } else {
257 | fprintf(stderr, "Successfully erased (0x%02x) configuration memory\n", recv[0]);
258 | }
259 | }
260 |
261 | void HIDOpenUPS::WriteConfigurationMemory()
262 | {
263 | unsigned char recv[32];
264 | int ret;
265 |
266 | m_ulSettingsAddr = OPENUPS_SETTINGS_ADDR_START;
267 | while (m_ulSettingsAddr < OPENUPS_SETTINGS_ADDR_END)
268 | {
269 | sendMessageWithBuffer(OPENUPS_MEM_WRITE_OUT, 16, m_chPackages+(m_ulSettingsAddr-OPENUPS_SETTINGS_ADDR_START), 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x10);
270 | ret = recvMessage(recv);
271 |
272 | if (ret <= 0 || recv[0] != OPENUPS_MEM_WRITE_IN) {
273 | fprintf(stderr, "Error (%d, 0x%02x) writing configuration variables , aborting ...\n", ret, recv[0]);
274 | break;
275 | } else {
276 | fprintf(stderr, "Wrote page 0x%lx of configuration\n", m_ulSettingsAddr);
277 | }
278 | m_ulSettingsAddr += 16;
279 | }
280 | }
281 |
282 | void HIDOpenUPS::parseMessage(unsigned char *msg)
283 | {
284 | if (!msg)
285 | return;
286 |
287 | switch (msg[0])
288 | {
289 | case OPENUPS_RECV_ALL_VALUES:
290 | {
291 | m_nVerMajor = (msg[31] >> 4) & 0x0F;
292 | m_nVerMinor = msg[31] & 0x0F;
293 |
294 | m_fVIN = convertOneValue2Float(msg, 2, 1, 20, GetConstant(0));
295 | m_fVOut = convertOneValue2Float(msg, 2, 3, 20, GetConstant(1));
296 | m_fVBat = convertOneValue2Float(msg, 2, 5, 2, GetConstant(2));
297 |
298 | for (int i=0;i<6;i++)
299 | m_fVCell[i] = convertOneValue2Float(msg, 2, 7+2*i, 2, GetConstant(2));
300 |
301 | for (int i=0;i<8;i++)
302 | {
303 | m_bCheck23[i] = (((msg[23] >> i)&1) == 1);
304 | m_bCheck24[i] = (((msg[24] >> i)&1) == 1);
305 | m_bCheck25[i] = (((msg[25] >> i)&1) == 1);
306 | }
307 | m_nOtherState[4] = msg[23];
308 | m_nOtherState[5] = msg[24];
309 | m_nOtherState[6] = msg[25];
310 |
311 | m_fCCharge = convertOneValue2Float(msg, 2, 19, 2, GetConstant(4)/(double)1000);
312 |
313 | if (m_bCheck24[6])
314 | {
315 | m_nState = 1;
316 |
317 | m_fCDischarge = convertOneValue2Float(msg, 2, 21, 2, GetConstant(5)/(double)1000);
318 | m_fCIn = 0;
319 | }
320 | else if (m_bCheck24[5])
321 | {
322 | m_nState = 2;
323 |
324 | double chg_current = convertOneValue2Float(msg, 2, 19, 2, GetConstant(4)/(double)1000);
325 | double vbat = convertOneValue2Float(msg, 2, 5, 2, GetConstant(2));
326 | double vin = convertOneValue2Float(msg, 2, 1, 20, GetConstant(0));
327 | double outbuckboost_input_current = convertOneValue2Float(msg, 2, 21, 2, GetConstant(5)/(double)1000);
328 |
329 | m_fCIn = chg_current * vbat / (vin * ETA_CHG) + outbuckboost_input_current;
330 | m_fCDischarge = 0;
331 | }
332 | else
333 | {//only usb
334 | m_nState = 3;
335 |
336 | m_fCDischarge = 0;
337 | m_fCIn = 0;
338 | }
339 |
340 | m_fTemperature = convertOneValue2Float(msg, 2, 26, 15, 1);
341 | }break;
342 | case OPENUPS_CLOCK_IN:
343 | {
344 | m_nYear = 2000+bcd2hex(msg[2]);
345 | m_nMonth = bcd2hex(msg[3]);
346 | m_nDay = bcd2hex(msg[4]);
347 |
348 | m_nHour = bcd2hex(msg[6]);
349 | m_nMin = bcd2hex(msg[7]);
350 | m_nSec = bcd2hex(msg[8]);
351 |
352 | m_nCapacity = msg[12];
353 | m_nRTE = msg[17];
354 | m_nRTE = (m_nRTE << 8) | msg[16];
355 |
356 | m_nOtherState[0] = msg[9]; //upsstate
357 | m_nOtherState[1] = msg[10];//charger state
358 | m_nOtherState[2] = msg[11];//out state
359 | m_nOtherState[3] = msg[15];//battery on
360 | }break;
361 | case OPENUPS_RECV_ALL_VALUES_2:
362 | {
363 | m_fOutputPower = convertOneValue2Float(msg, 4, 1, 16, 0.000001);
364 | }break;
365 |
366 | case OPENUPS_MEM_READ_IN:
367 | {
368 | memcpy(m_chPackages + (m_ulSettingsAddr - OPENUPS_SETTINGS_ADDR_START), msg + 5, 16);
369 | /*
370 | fprintf(stderr, "%02lx %02lx", m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF);
371 | for (int len = 0; len < 16; len++)
372 | fprintf(stderr, " %02x", msg[5 + len]);
373 | fprintf(stderr, "\n");
374 | */
375 | }
376 | break;
377 | }
378 | }
379 |
380 | float HIDOpenUPS::convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier)
381 | {
382 | double value = -1;
383 |
384 | if (nLen == 1)
385 | value = (double)buffer[nIndex] * dMultiplier;
386 | else if (nLen == 2)
387 | value = (double)( (((int)buffer[nIndex+1] ) << 8)|buffer[nIndex]) * dMultiplier;
388 | else if (nLen == 4)
389 | value = (double)( (((int)buffer[nIndex+3] ) << 24) | (((int)buffer[nIndex+2] ) << 16) | (((int)buffer[nIndex+1] ) << 8) | buffer[nIndex]) * dMultiplier;
390 |
391 | switch (nReadMode)
392 | {
393 | case 10:
394 | {
395 | if (nLen == 1)
396 | value = (double)buffer[nIndex] * GetConstant(dMultiplier);
397 | else
398 | value = (double)( (((int)buffer[nIndex+1] ) << 8)|buffer[nIndex]) * GetConstant(dMultiplier);
399 | }break;
400 | case 15:
401 | {
402 | unsigned int termistor = 0;
403 | termistor = buffer[nIndex+1];
404 | termistor = (termistor << 8) | buffer[nIndex];
405 |
406 | if (termistor <= GetTermistorConsts()[0])
407 | value = -40;
408 | else if (termistor >= GetTermistorConsts()[TERMISTOR_CONSTS-1])
409 | value = 125;
410 | else
411 | {
412 | int pos = -1;
413 | for (int i=TERMISTOR_CONSTS-1;i>=0;i--)
414 | {
415 | if (termistor >= GetTermistorConsts()[i])
416 | {
417 | pos = i;
418 | break;
419 | }
420 | }
421 |
422 | if (termistor == GetTermistorConsts()[pos])
423 | value = pos*5-40;
424 | else
425 | {
426 | int t1 = pos*5-40;
427 | int t2 = (pos+1)*5-40;
428 |
429 | unsigned int d1 = GetTermistorConsts()[pos];
430 | unsigned int d2 = GetTermistorConsts()[pos+1];
431 |
432 | float dtemp = ((float)termistor - (float)d1)*((float)t2-(float)t1)/((float)d2-(float)d1);
433 |
434 | int temp = ceil(dtemp) + t1;
435 |
436 | value = temp;
437 | }
438 | }
439 | }break;
440 | }
441 |
442 | return value;
443 | }
444 |
445 | bool HIDOpenUPS::readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4)
446 | {
447 | bool ok = false;
448 | c1 = 0;
449 | c2 = 0;
450 |
451 | double val = 0;
452 |
453 | switch (nReadMode)
454 | {
455 | case 3:
456 | {//hh:mm:ss
457 | if (strcmp(str, "Never") == 0)
458 | {
459 | val = 0xFFFF;
460 | ok = true;
461 | }
462 | else if ((strlen(str) == 8) && (str[2]==':') && (str[5]==':'))
463 | {
464 | char* end = NULL;
465 | char temp[10];
466 | str_left(temp, str, 2);
467 | int hh = _tcstol(temp, &end, 10);
468 | if ((end != temp ) && (hh>=0) && (hh<18))
469 | {
470 | str_mid(temp, str, 3,2);
471 | int mm = _tcstol(temp, &end, 10);
472 | if ((end != temp) && (mm>=0) && (mm<60))
473 | {
474 | str_right(temp, str, 2);
475 | int ss = _tcstol(temp, &end, 10);
476 |
477 | if ((end != temp) && (ss>=0) && (ss<60))
478 | {
479 | val = hh*3600+mm*60+ss;
480 | ok = true;
481 | }
482 | }
483 | }
484 | }
485 | }break;
486 | case 4:
487 | {//"signed" float
488 | if (strlen(str) != 0)
489 | {
490 | bool negative = false;
491 | char* end = NULL;
492 | char temp[10];
493 |
494 | if (str[0] == '-')
495 | {
496 | str_right(temp, str, strlen(str) - 1);
497 | str = temp;
498 | negative = true;
499 | }
500 | str_left(temp, str, 8);
501 | val = _tcstod(temp, &end);
502 |
503 | if (end != temp)
504 | {
505 | ok = true;
506 |
507 | val /= dMultiplier;
508 | if (val < 0) val = 0;
509 | double rem = val - (int) val;
510 | unsigned long uval = (unsigned long)val;
511 | if (rem >= 0.5) uval += 1;
512 |
513 | if (uval > 0x7F) uval = 0x7F;
514 |
515 | c1 = 0;
516 | c2 = (unsigned char)uval;
517 | if (negative) c2 |= 0x80;
518 | }
519 | }
520 | }break;
521 | case 6:
522 | {
523 | val = 0xFF;
524 | ok = true;
525 | }break;
526 | case 7:
527 | {
528 | char* end = NULL;
529 | char temp[10];
530 | str_left(temp, str, 8);
531 | val = _tcstod(temp, &end);
532 |
533 | c2 = 0;
534 | c1 = GetDataVOut(val);
535 | ok = true;
536 | }break;
537 | case 8:
538 | {
539 | if (strcmp(str, "Never") == 0)
540 | {
541 | val = (double)0xFFFF * dMultiplier;
542 | ok = true;
543 | }
544 | else
545 | {
546 | char* end = NULL;
547 | char temp[10];
548 |
549 | str_left(temp, str, 8);
550 | val = _tcstod(temp, &end);
551 | if (end != temp) ok = true;
552 | }
553 | }break;
554 | case 9:
555 | {
556 | char* end = NULL;
557 | char temp[10];
558 | str_left(temp, str, 8);
559 | val = _tcstod(temp, &end);
560 |
561 | c1 = 0;
562 | c2 = GetDataVBat(val);
563 | ok = true;
564 | }break;
565 | case 11:
566 | {
567 | int cnt = atoi(str);
568 | if ((cnt>=0) && (cnt<32))
569 | {
570 | c1 = GetUVPs()[cnt].nCVR_2;
571 | c2 = GetUVPs()[cnt].nCVR_1;
572 | ok = true;
573 | }
574 | }break;
575 | case 12:
576 | case 13:
577 | {
578 | c1 = 0;
579 | c2 = atoi(str);
580 | ok = true;
581 | }break;
582 | case 14:
583 | {
584 | c1 = 0;
585 | c2 = bin2char(str, &ok);
586 | }break;
587 | case 15:
588 | {
589 | unsigned int termistor = 0;
590 |
591 | int temp = atoi(str);
592 | if (temp <= -40) termistor = GetTermistorConsts()[0];
593 | else if (temp >= 125) termistor = GetTermistorConsts()[TERMISTOR_CONSTS-1];
594 | else
595 | {
596 | int pos = (temp + 40) / 5;
597 | int temp_dif = (temp + 40) % 5;
598 |
599 | if (temp_dif == 0)
600 | termistor = GetTermistorConsts()[pos];
601 | else
602 | {
603 | unsigned int d1 = GetTermistorConsts()[pos];
604 | unsigned int d2 = GetTermistorConsts()[pos+1];
605 |
606 | termistor = d1 + temp_dif * (d2-d1) / 5;
607 | }
608 | }
609 |
610 | c1 = (unsigned char)(((unsigned int)termistor) & 0xFF);
611 | c2 = (unsigned char)((((unsigned int)termistor)>>8) & 0xFF);
612 | ok = true;
613 | }break;
614 | case 16:
615 | {
616 | TCHAR* end = NULL;
617 |
618 | val = _tcstod(str, &end);
619 |
620 | if ((val != 0) && (end != str))
621 | {
622 | val = ceil(val / dMultiplier);
623 | unsigned int ival = (unsigned int) val;
624 |
625 | c1 = (unsigned char)( ival & 0xFF);
626 | c2 = (unsigned char)((ival>>8 ) & 0xFF);
627 | c3 = (unsigned char)((ival>>16) & 0xFF);
628 | c4 = (unsigned char)((ival>>24) & 0xFF);
629 | ok = true;
630 | }
631 | }break;
632 | case 17:
633 | {
634 | char* end = NULL;
635 | char temp[10];
636 | str_left(temp, str, 8);
637 | val = _tcstod(temp, &end);
638 | if (end != temp) ok = true;
639 | if ((val != 0)&&(val != 1)) ok = false;
640 |
641 | }break;
642 | case 1:
643 | //normal read - integer
644 | case 2:
645 | case 5:
646 | default:
647 | {//normal read - float
648 | char* end = NULL;
649 | char temp[10];
650 | str_left(temp, str, 8);
651 | val = _tcstod(temp, &end);
652 | if (end != temp) ok = true;
653 | }
654 | }
655 |
656 | if (ok)
657 | {
658 | if ((nReadMode != 4)&&(nReadMode != 7)&&(nReadMode != 9)&&(nReadMode != 11)&&(nReadMode != 12)&&(nReadMode != 13)&&(nReadMode != 14)&&(nReadMode != 15)&&(nReadMode != 16))
659 | {
660 | if (nReadMode == 10)
661 | val /= GetConstant(dMultiplier);
662 | else
663 | val /= dMultiplier;
664 |
665 | if (nReadMode!=5)
666 | {//unsigned
667 | if (val < 0) val = 0;
668 | double rem = val - (int) val;
669 | unsigned long uval = (unsigned long)val;
670 | if (rem >= 0.5) uval += 1;
671 |
672 | if (len == 1)
673 | {
674 | if (uval > 0xFF) uval = 0xFF;
675 |
676 | c1 = 0;
677 | c2 = (unsigned char)uval;
678 | }
679 | else
680 | {
681 | if (uval > 0xFFFF) uval = 0xFFFF;
682 |
683 | c1 = (unsigned char)(((unsigned int)uval) & 0xFF);
684 | c2 = (unsigned char)((((unsigned int)uval)>>8) & 0xFF);
685 | }
686 | }
687 | else
688 | {
689 | double rem = -val + (int) val;
690 | long uval = (long)val;
691 | if (rem >= 0.5) uval += 1;
692 |
693 | if (len == 1)
694 | {
695 | if (uval > 127) uval = 127;
696 | if (uval <-128) uval = -128;
697 |
698 | c1 = 0;
699 | c2 = (char)uval;
700 | }
701 | else
702 | {
703 | if (uval > 32767) uval = 32767;
704 | if (uval <-32768) uval = -32768;
705 |
706 | c1 = (char)(((int)uval) & 0xFF);
707 | c2 = (char)((((int)uval)>>8) & 0xFF);
708 | }
709 | }
710 | }
711 | }
712 |
713 | return ok;
714 | }
715 |
716 | void HIDOpenUPS::convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier)
717 | {
718 | destination[0] = 0; //empty string
719 | double value = 0;
720 |
721 | if (nLen == 1)
722 | value = (double)m_chPackages[nIndex] * dMultiplier;
723 | else if (nLen == 2)
724 | value = (double)( (((int)m_chPackages[nIndex+1] ) << 8)|m_chPackages[nIndex]) * dMultiplier;
725 | else if (nLen == 4)
726 | value = (double)( (((int)m_chPackages[nIndex+3] ) << 24) | (((int)m_chPackages[nIndex+2] ) << 16) | (((int)m_chPackages[nIndex+1] ) << 8) | m_chPackages[nIndex]) * dMultiplier;
727 |
728 | switch (nReadMode)
729 | {
730 | case 2: sprintf(destination, "%f", (float)value); break;
731 | case 20: sprintf(destination, "%.2f", (float)value); break;
732 | case 10:
733 | {
734 | if (nLen == 1)
735 | value = (double)m_chPackages[nIndex] * GetConstant(dMultiplier);
736 | else if (nLen == 2)
737 | value = (double)( (((int)m_chPackages[nIndex+1] ) << 8)|m_chPackages[nIndex]) * GetConstant(dMultiplier);
738 | else if (nLen == 4)
739 | value = (double)( (((int)m_chPackages[nIndex+3] ) << 24) | (((int)m_chPackages[nIndex+2] ) << 16) | (((int)m_chPackages[nIndex+1] ) << 8) | m_chPackages[nIndex]) * GetConstant(dMultiplier);
740 | sprintf(destination, "%f", (float)value);
741 | }break;
742 | case 11:
743 | {
744 | int pos = -1;
745 | for (int i=0;i<32;i++)
746 | if ( (GetUVPs()[i].nCVR_1 == m_chPackages[nIndex+1])
747 | &&(GetUVPs()[i].nCVR_2 == m_chPackages[nIndex]))
748 | pos = i;
749 |
750 | sprintf(destination, "%d", pos);
751 | }break;
752 | case 12:
753 | case 13:
754 | sprintf(destination, "%d", m_chPackages[nIndex]);
755 | break;
756 | case 14:
757 | char2bin(destination, m_chPackages[nIndex]);
758 | break;
759 | case 15:
760 | {
761 | unsigned int termistor = 0;
762 | termistor = m_chPackages[nIndex+1];
763 | termistor = (termistor << 8) | m_chPackages[nIndex];
764 |
765 | if (termistor <= GetTermistorConsts()[0])
766 | sprintf(destination, "%s", "-40");
767 | else if (termistor >= GetTermistorConsts()[TERMISTOR_CONSTS-1])
768 | sprintf(destination, "%s", "125");
769 | else
770 | {
771 | int pos = -1;
772 | for (int i=TERMISTOR_CONSTS-1;i>=0;i--)
773 | {
774 | if (termistor >= GetTermistorConsts()[i])
775 | {
776 | pos = i;
777 | break;
778 | }
779 | }
780 |
781 | if (termistor == GetTermistorConsts()[pos])
782 | sprintf(destination, "%d", pos * 5-40);
783 | else
784 | {
785 | int t1 = pos*5-40;
786 | int t2 = (pos+1)*5-40;
787 |
788 | unsigned int d1 = GetTermistorConsts()[pos];
789 | unsigned int d2 = GetTermistorConsts()[pos+1];
790 |
791 | float dtemp = ((float)termistor - (float)d1)*((float)t2-(float)t1)/((float)d2-(float)d1);
792 |
793 | int temp = ceil(dtemp) + t1;
794 |
795 | sprintf(destination, "%d", temp);
796 | }
797 | }
798 | }break;
799 | case 16:
800 | {//4 byte value
801 | if (nLen == 1)
802 | value = (double)m_chPackages[nIndex];
803 | else if (nLen == 2)
804 | value = (double)( (((int)m_chPackages[nIndex+1] ) << 8)|m_chPackages[nIndex]);
805 | else if (nLen == 4)
806 | value = (double)( (((int)m_chPackages[nIndex+3] ) << 24) | (((int)m_chPackages[nIndex+2] ) << 16) | (((int)m_chPackages[nIndex+1] ) << 8) | m_chPackages[nIndex]);
807 |
808 | value = value * dMultiplier;
809 |
810 | if (dMultiplier == 1)
811 | sprintf(destination, "%.0f", (float)value);
812 | else
813 | sprintf(destination, "%.2f", (float)value);
814 | }break;
815 | case 17:
816 | {
817 | sprintf(destination, "%d", (int)value);
818 | }break;
819 | case 3:
820 | {
821 | unsigned int ivalue = (unsigned int)value;
822 | if (ivalue == 0xFFFF)
823 | sprintf(destination, "%s", "Never");
824 | else
825 | sprintf(destination, "%02d:%02d:%02d"
826 | , ivalue/3600
827 | , (ivalue/60)%60
828 | , ivalue % 60
829 | );
830 | }break;
831 | case 4:
832 | {
833 | double dvalue = (m_chPackages[nIndex] & 0x7f) * dMultiplier;
834 | if ((m_chPackages[nIndex] & 0x80)!=0)
835 | dvalue *= -1;
836 |
837 | sprintf(destination, "%f", (float)dvalue);
838 | }break;
839 | case 5:
840 | {
841 | if (nLen == 1)
842 | value = (double)((char)m_chPackages[nIndex]) * dMultiplier;
843 | else
844 | value = (double)( (((int)((char)m_chPackages[nIndex+1]) ) << 8)|m_chPackages[nIndex])
845 | * dMultiplier;
846 | sprintf(destination, "%d", (int)value);
847 | }break;
848 | case 6:
849 | {
850 | sprintf(destination, "%s", "-");
851 | }break;
852 | case 7:
853 | {//no multiplier - voltage value calcualted with formula
854 | sprintf(destination, "%f", GetVoltageVOut(m_chPackages[nIndex]));
855 | }break;
856 | case 8:
857 | {
858 | if (nLen == 1)
859 | {
860 | if (m_chPackages[nIndex] == 0xFF)
861 | sprintf(destination, "%s", "Never");
862 | else
863 | sprintf(destination, "%d", (int)value);
864 | }
865 | else
866 | {
867 | if ((m_chPackages[nIndex] == 0xFF) && (m_chPackages[nIndex+1] == 0xFF))
868 | sprintf(destination, "%s", "Never");
869 | else
870 | sprintf(destination, "%d", (int)value);
871 | }
872 | }break;
873 | case 9:
874 | {//no multiplier - voltage value calcualted with formula
875 | sprintf(destination, "%f", GetVoltageVBat(m_chPackages[nIndex], m_chPackages[nIndex+1]));
876 | }break;
877 | case 1:
878 | default:
879 | {
880 | sprintf(destination, "%d", (int)value);
881 | }
882 | }
883 |
884 | }
885 |
886 | bool HIDOpenUPS::setVariableData(int mesg_no, char *str)
887 | {
888 | int len = GetMessages()[mesg_no].nLen;
889 |
890 | if (len !=0)
891 | {
892 | unsigned char c1 = 0;
893 | unsigned char c2 = 0;
894 | unsigned char c3 = 0;
895 | unsigned char c4 = 0;
896 |
897 | if (readOneValue(str
898 | ,GetMessages()[mesg_no].nReadMode
899 | ,GetMessages()[mesg_no].dMultiplier
900 | ,len
901 | ,c1, c2, c3, c4))
902 | {
903 | switch (len)
904 | {
905 | case 1:
906 | m_chPackages[GetMessages()[mesg_no].nIndex] = c2;
907 | break;
908 | case 2:
909 | m_chPackages[GetMessages()[mesg_no].nIndex] = c1;
910 | m_chPackages[GetMessages()[mesg_no].nIndex+1] = c2;
911 | break;
912 | case 4:
913 | m_chPackages[GetMessages()[mesg_no].nIndex] = c1;
914 | m_chPackages[GetMessages()[mesg_no].nIndex+1] = c2;
915 | m_chPackages[GetMessages()[mesg_no].nIndex+2] = c3;
916 | m_chPackages[GetMessages()[mesg_no].nIndex+3] = c4;
917 | break;
918 | }
919 | return true;
920 | }
921 | else
922 | {
923 | return false;
924 | }
925 | }
926 | return false;
927 | }
928 |
929 | unsigned char HIDOpenUPS::getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment)
930 | {
931 | if ((cnt < 0) || (cnt >= MAX_MESSAGE_CNT))
932 | return 0;
933 |
934 | if (GetMessages()[cnt].nLen == 0)
935 | return 0;
936 |
937 | if (name)
938 | strcpy(name, GetMessages()[cnt].strName);
939 | if (unit)
940 | strcpy(unit, GetMessages()[cnt].strUnit);
941 | if (comment)
942 | strcpy(comment, GetMessages()[cnt].strText);
943 |
944 | if (value)
945 | {
946 | convertOneValue2String(value, GetMessages()[cnt].nLen,
947 | GetMessages()[cnt].nIndex,
948 | GetMessages()[cnt].nReadMode,
949 | GetMessages()[cnt].dMultiplier);
950 | }
951 |
952 | return GetMessages()[cnt].nReadMode;
953 | }
954 |
955 | void HIDOpenUPS::restartUPS()
956 | {
957 | sendCommand(OPENUPS_DCMD_RESTART_UPS, 1);
958 | }
959 |
960 | void HIDOpenUPS::restartUPSInBootloaderMode()
961 | {
962 |
963 | }
964 |
965 | double HIDOpenUPS::GetVoltageVOut(unsigned char data)
966 | {
967 | double rpot = ((double)data) * CT_RP / (double)257 + CT_RW;
968 | double voltage = (double)80 * ( (double)1 + CT_R1_VOUT/(rpot+CT_R2_VOUT));
969 | voltage = floor(voltage);
970 | return voltage/100;
971 | }
972 |
973 | unsigned char HIDOpenUPS::GetDataVOut(double vout)
974 | {
975 | if (vout < 0.9) return (unsigned char)0xff;//low limit
976 |
977 | double rpot = (double)0.8 * CT_R1_VOUT / (vout - (double)0.8) - CT_R2_VOUT;
978 | double result = (257 * (rpot-CT_RW) / CT_RP);
979 |
980 | if (result<0) result = 0;
981 | if (result>255) result = 255;
982 |
983 | return (unsigned char)result;
984 | }
985 |
986 | double HIDOpenUPS::GetVoltageVBat(unsigned char rp1, unsigned rp2)
987 | {
988 | double voltage = ( (double)1 + CT_R1_VBAT / ((double)(rp1*CT_LSB_RP1)+(double)(rp2*CT_LSB_RP2)+CT_R2_VBAT)) * (double)123;
989 | voltage = floor(voltage);
990 | return voltage/100;
991 | }
992 |
993 | unsigned short HIDOpenUPS::GetDataVBat(double vout)
994 | {
995 | if (vout < 1.24) return 0x7fff;//low limit
996 |
997 | double rpot = ((double)1.23 * (CT_R1_VBAT + CT_R2_VBAT) - vout * CT_R2_VBAT) / (vout - (double)1.23);
998 |
999 | int r1 = (int) (rpot / CT_LSB_RP1);
1000 | r1 -= 6;//leave something to R2
1001 | if (r1<0) r1 = 0;
1002 | else if (r1>127) r1 = 127;
1003 |
1004 | double remainder = rpot - (double)r1*CT_LSB_RP1;
1005 | int r2 = (int) (remainder / CT_LSB_RP2);
1006 | if (r2<0) r2 = 0;
1007 | else if (r2>255) r2 = 255;
1008 |
1009 | unsigned short result = (r1 << 8)|r2;
1010 | return (unsigned short)result;
1011 | }
1012 |
--------------------------------------------------------------------------------
/src/lib/HIDOpenUPS.h:
--------------------------------------------------------------------------------
1 | #ifndef _HIDOPENUPS_H_
2 | #define _HIDOPENUPS_H_
3 |
4 | #include "HIDInterface.h"
5 | #include "usbhid.h"
6 |
7 | #include "HArray.h"
8 |
9 | #define ETA_CHG (double)0.90
10 |
11 | #define STATUS_OK 0x00
12 | #define STATUS_ERASE 0x01
13 | #define STATUS_WRITE 0x02
14 | #define STATUS_READ 0x03
15 | #define STATUS_ERROR 0xff
16 |
17 | #define OPENUPS_GET_ALL_VALUES 0x81
18 | #define OPENUPS_RECV_ALL_VALUES 0x82
19 | #define OPENUPS_CLOCK_OUT 0x83
20 | #define OPENUPS_CLOCK_IN 0x84
21 | #define OPENUPS_GET_ALL_VALUES_2 0x85
22 | #define OPENUPS_RECV_ALL_VALUES_2 0x86
23 | #define OPENUPS_CMD_OUT 0xB1
24 | #define OPENUPS_CMD_IN 0xB2
25 | #define OPENUPS_MEM_READ_OUT 0xA1
26 | #define OPENUPS_MEM_READ_IN 0xA2
27 | #define OPENUPS_MEM_WRITE_OUT 0xA3
28 | #define OPENUPS_MEM_WRITE_IN 0xA4
29 | #define OPENUPS_MEM_ERASE 0xA5
30 |
31 | #define OPENUPS_ENTER_CALIBRATION_OUT 0xA9
32 | #define OPENUPS_ENTER_CALIBRATION_IN 0xAA
33 |
34 | #define INTERNAL_MESG 0xFF
35 | #define INTERNAL_MESG_DISCONNECTED 0x01
36 |
37 | //commands
38 | #define OPENUPS_DCMD_3V3LP 0x01
39 | #define OPENUPS_DCMD_ENABLE_VINPATH 0x02
40 | #define OPENUPS_DCMD_ENABLE_BATTERYPATH 0x03
41 | #define OPENUPS_DCMD_ENABLE_OUTPUT 0x04
42 | #define OPENUPS_DCMD_PWM_OUT 0x05
43 | #define OPENUPS_DCMD_PSW 0x06
44 | #define OPENUPS_DCMD_ENABLE_CHARGER 0x07
45 | #define OPENUPS_DCMD_PWM_CHARGER 0x08
46 | #define OPENUPS_DCMD_CHG_FB 0x09
47 | #define OPENUPS_DCMD_BULK_CHARGE 0x0A
48 |
49 | #define OPENUPS_DCMD_CELL1_DISCHARGE 0x11
50 | #define OPENUPS_DCMD_CELL2_DISCHARGE 0x12
51 | #define OPENUPS_DCMD_CELL3_DISCHARGE 0x13
52 | #define OPENUPS_DCMD_CELL4_DISCHARGE 0x14
53 | #define OPENUPS_DCMD_CELL5_DISCHARGE 0x15
54 | #define OPENUPS_DCMD_CELL6_DISCHARGE 0x16
55 |
56 | #define OPENUPS_DCMD_SPI_CS_AD 0x21
57 | #define OPENUPS_DCMD_SPI_CS_VBAT 0x22
58 | #define OPENUPS_DCMD_SPI_CS_TEMP 0x23
59 | #define OPENUPS_DCMD_SPI_CS_VOUT 0x24
60 | #define OPENUPS_DCMD_SPI_CLK 0x25
61 | #define OPENUPS_DCMD_SPI_DATAOUT 0x26
62 |
63 | #define OPENUPS_DCMD_SPI_WRITE_DATA_VOL_VOUT 0x30
64 | #define OPENUPS_DCMD_SPI_READ_DATA_VOL_VOUT 0x31
65 | #define OPENUPS_DCMD_SPI_INC_VOL_WIPER_VOUT 0x32
66 | #define OPENUPS_DCMD_SPI_DEC_VOL_WIPER_VOUT 0x33
67 | #define OPENUPS_DCMD_SPI_VOUT_STAT 0x34
68 | #define OPENUPS_DCMD_SPI_VOUT_TCON 0x35
69 |
70 | #define OPENUPS_DCMD_SPI_READ_AD_CHANNEL 0x36
71 |
72 | #define OPENUPS_DCMD_SPI_WRITE_DATA_VOL_VCHG 0x40
73 | #define OPENUPS_DCMD_SPI_READ_DATA_VOL_VCHG 0x41
74 | //#define OPENUPS_DCMD_SPI_INC_VOL_WIPER_VCHG 0x42
75 | //#define OPENUPS_DCMD_SPI_DEC_VOL_WIPER_VCHG 0x43
76 | #define OPENUPS_DCMD_I2C_INC_VOL_WIPER_VCHG_ROUGH 0x45
77 | #define OPENUPS_DCMD_I2C_DEC_VOL_WIPER_VCHG_ROUGH 0x46
78 | #define OPENUPS_DCMD_I2C_INC_VOL_WIPER_VCHG_FINE 0x47
79 | #define OPENUPS_DCMD_I2C_DEC_VOL_WIPER_VCHG_FINE 0x48
80 |
81 | #define OPENUPS_DCMD_LED 0x50
82 |
83 | #define OPENUPS_DCMD_SET_UVP_LOW 0x70
84 | #define OPENUPS_DCMD_SET_UVP_HIGH 0x71
85 |
86 | #define OPENUPS_DCMD_SET_OUTPUT_FREQUENCY 0x72
87 | #define OPENUPS_DCMD_SET_CHARGE_FREQUENCY 0x73
88 |
89 | #define OPENUPS_DCMD_RESTART_UPS 0xAA
90 |
91 | //For reading out memory
92 | #define TYPE_CODE_MEMORY 0x00
93 | #define TYPE_EPROM_EXTERNAL 0x01
94 | #define TYPE_EPROM_INTERNAL 0x02
95 | #define TYPE_CODE_SPLASH 0x03
96 |
97 | #define FLASH_REPORT_ERASE_MEMORY 0xF2 // AddressLo : AddressHi : AddressUp (anywhere inside the 64 byte-block to be erased)
98 | #define FLASH_REPORT_READ_MEMORY 0xF3 // AddressLo : AddressHi : AddressUp : Data Length (1...32)
99 | #define FLASH_REPORT_WRITE_MEMORY 0xF4 // AddressLo : AddressHi : AddressUp : Data Length (1...32) : Data....
100 | #define KEYBD_REPORT_ERASE_MEMORY 0xB2 // same as F2 but in keyboard mode
101 | #define KEYBD_REPORT_READ_MEMORY 0xB3 // same as F3 but in keyboard mode
102 | #define KEYBD_REPORT_WRITE_MEMORY 0xB4 // same as F4 but in keyboard mode
103 | #define KEYBD_REPORT_MEMORY 0x41 // response to b3,b4
104 |
105 | #define IN_REPORT_EXT_EE_DATA 0x31
106 | #define OUT_REPORT_EXT_EE_READ 0xA1
107 | #define OUT_REPORT_EXT_EE_WRITE 0xA2
108 |
109 | #define IN_REPORT_INT_EE_DATA 0x32
110 | #define OUT_REPORT_INT_EE_READ 0xA3
111 | #define OUT_REPORT_INT_EE_WRITE 0xA4
112 |
113 | ///// MEASUREMENT CONSTANTS
114 | #define CT_RW (double)75
115 | #define CT_RP (double)5000
116 | #define CT_R1_VOUT (double)27000//24900
117 | #define CT_R2_VOUT (double)750
118 |
119 | #define CT_R1_VBAT (double)150000
120 | #define CT_R2_VBAT (double)4990
121 | #define CT_RP1_VBAT (double)50000
122 | #define CT_RP2_VBAT (double)5000
123 | #define CT_LSB_RP1 (double)(CT_RP1_VBAT/129)
124 | #define CT_LSB_RP2 (double)(CT_RP2_VBAT/257)
125 |
126 | #define CHECK_CHAR (unsigned char)0xAA //used for line/write check
127 |
128 | #define MAX_MESSAGE_CNT 256
129 |
130 | #define TERMISTOR_CONSTS 34
131 |
132 | #define OPENUPS_SETTINGS_ADDR_START 0x007800
133 | #define OPENUPS_SETTINGS_ADDR_END 0x007C00
134 | #define OPENUPS_SETTINGS_ADDR_CALIBR_START 0x0078D0
135 | #define OPENUPS_SETTINGS_ADDR_CALIBR_END 0x007900
136 | #define OPENUPS_SETTINGS_PACKS 64
137 | #define OPENUPS_SCRIPTS_PACKS 4
138 | #define OPENUPS_SCRIPT_START_ADDR 0x3C00//3F80
139 | #define OPENUPS_SCRIPT_END_ADDR 0x3BF0
140 |
141 |
142 | class HIDOpenUPS : public HIDInterface
143 | {
144 | public:
145 | HIDOpenUPS(USBHID *d);
146 | virtual ~HIDOpenUPS();
147 |
148 | void GetStatus();
149 | void ReadConfigurationMemory();
150 | void EraseConfigurationMemory();
151 | void WriteConfigurationMemory();
152 | void parseMessage(unsigned char *msg);
153 | void printValues();
154 | float convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier);
155 | bool readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4);
156 | void convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier);
157 | bool setVariableData(int mesg_no, char *str);
158 | UVP* GetUVPs();
159 | ATXMSG* GetMessages();
160 | double GetConstant(int i);
161 | unsigned int* GetTermistorConsts();
162 | unsigned char getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment);
163 | void restartUPS();
164 | void restartUPSInBootloaderMode();
165 |
166 | double GetVoltageVOut(unsigned char data);
167 | unsigned char GetDataVOut(double vout);
168 | double GetVoltageVBat(unsigned char rp1, unsigned rp2);
169 | unsigned short GetDataVBat(double vout);
170 |
171 | private:
172 | float m_fVIN;
173 | float m_fVBat;
174 | float m_fVOut;
175 | float m_fCCharge;
176 | float m_fCDischarge;
177 | float m_fCIn;
178 | float m_fTemperature;
179 | float m_fVCell[6];
180 | bool m_bCheck23[8];
181 | bool m_bCheck24[8];
182 | bool m_bCheck25[8];
183 |
184 | unsigned char m_nVerMajor;
185 | unsigned char m_nVerMinor;
186 |
187 | unsigned char m_nState;
188 |
189 | unsigned int m_nYear;
190 | unsigned int m_nMonth;
191 | unsigned int m_nDay;
192 |
193 | unsigned int m_nHour;
194 | unsigned int m_nMin;
195 | unsigned int m_nSec;
196 |
197 | unsigned int m_nCapacity;
198 | unsigned int m_nRTE;
199 |
200 | float m_fOutputPower;
201 |
202 | unsigned char m_nOtherState[7];
203 | };
204 |
205 | #endif
206 |
--------------------------------------------------------------------------------
/src/lib/HIDOpenUPS2.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | */
7 |
8 | #include "util.h"
9 | #include "HIDOpenUPS2.h"
10 |
11 | #include
12 |
13 | double g_OpenUPS2_memConstants[] =
14 | {
15 | 0.001, //Vin //0
16 | 0.001, //Vout //1
17 | 0.001, //Vbat+chg //2
18 | 0.001, //A chg Hi range //3
19 | 0.001, //A chg Low range //4
20 | 0.001, //A discharge //5
21 | 0.001 //battery
22 | };
23 |
24 | ATXMSG g_OpenUPS2_memMessages[MAX_MESSAGE_CNT] =
25 | {
26 | {67, 1, _T("OPENUPSMODE"), true, 17, 1, 0, 0, _T("OPENUPS mode\r\n0-Autorestart when input is present=yes\t(UPS)\r\n1-Autorestart when input is present=no\t(LAPTOP)\r\n2-Energy Pack mode\t\t\t(ON/OFF)"), _T("[-]")},
27 | {28, 4, _T("CAPACITY"), true, 16, 1, 0, 0, _T("Battery Capacity.\r\nDefault 1500mAh"), _T("[mAh]")},
28 | {80, 2, _T("MOB_ONOFF_TOUT"), true, 1, 10, 0, 0, _T("The powerswitch on the motherboard(if connected) will be shorted this period to turn the Motherboard ON or OFF\r\nDefault is 500ms"), _T("[mS]")},
29 | {66, 1, _T("UPS_BUTTON_ON_TOUT"), true, 1, 10, 0, 0, _T("The pushbutton connected on the P10 header must be shorted this period to be considered as pressed.\r\nDefault 100ms."), _T("[mS]")},
30 |
31 | {0, 1, _T("UPS_CONFIG"), true, 14, 1, 0, 0, _T("Configuration register. Used for enabling disabling modules.0-disabled;1-enabled\r\nbit7:reserved\r\nbit6:reserved\r\nbit5:reserved\r\nbit4:Enable No Battery Mode (FW 1.4+)\r\nbit3:reserved\r\nbit2:Enable temperature compensated charge algorithm\r\nbit1:Enable shutdown from battery if system was shut down from OS sensed by USB\r\nbit0:Enable output voltage settings directly from OUT_VOLTAGE parameter. If disabled output voltage is set by configuration switch on the back side of the PCBA."), _T("[bit7..bit0]")},
32 | {100, 1, _T("UPS_CONFIG2"), true, 14, 0, 0, 1, _T("Configuration register.Used for enabling disabling modules.0-disabled;1-enabled\r\nbit7:reserved\r\nbit6:reserved\r\nbit5:reserved\r\nbit4:reserved\r\nbit3:reserved\r\nbit2:reserved\r\nbit1:Enable Ship mode, consumption is less than 1uA. Unit can be waked up only by input power presence.\r\nbit0:Enable Sleep mode, consumption is 530uA. Unit can be waked up by button or input power presence.\r\nThe combination of bit1 ,bit0 sets the configurable low power modes\r\n01: Sleep mode CPU OFF (530uA)\r\n10: Ship mode CPU OFF(1uA)\r\n11 or 00: Standby mode CPU ON(15mA) ==> Ship mode CPU OFF(1uA)"), _T("[bit7..bit0]")},
33 |
34 | // { 1, 1, _T("UPS_INIT_DELAY_TOUT") , true, 1, 1, 0, 0 , _T("Initial delay before starting the UPS\r\nDefault is 1 sec."), _T("[s]") },
35 | // { 2, 2, _T("UPS_VIN_MAX_SHUTDOWN") , true,10, 0, 0, 0 , _T("Max allowed input voltage. In case input voltage exceeds predefined value shutdown will be initiated.\r\nDefault is 25V."), _T("[V]") },
36 | {4, 2, _T("UPS_VIN_MIN_START"), true, 10, 0, 0, 0, _T("If input voltage is above this threshold the UPS will start and take power from the input.\r\nDefault is 11V."), _T("[V]")},
37 | // { 6, 2, _T("UPS_VIN_MIN_STOP") , true,10, 0, 0, 0 , _T("If input voltage is below this threshold the UPS will try to run on battery.\r\nDefault is 6V."), _T("[V]") },
38 | {8, 2, _T("UPS_VCELL_MIN_START"), true, 10, 6, 0, 0, _T("If Vin is not present and all the battery cells are above this threshold the UPS can start and runs on battery\r\nDefault is 3V."), _T("[V]")},
39 | {10, 2, _T("UPS_VCELL_MIN_STOP"), true, 10, 6, 0, 0, _T("If VIN is not present and one of the battery cells is less than this threshold during the time period specified by UPS_VBAT_UVP_OFF_TOUT parameter the UPS will initiate shut down procedure.\r\nDefault is 2.8V"), _T("[V]")},
40 | /**/ {16, 2, _T("UPS_VCELL_MIN_HARD_STOP"), true, 10, 6, 0, 0, _T("If VIN is not present and one of the battery cells is less than this threshold the UPS will instantly shut down to preserve battery.\r\nDefault is 2V "), _T("[V]")},
41 | {12, 2, _T("UPS_VBAT_UVP_OFF_TOUT"), true, 1, 1, 0, 0, _T("During this period battery cells are checked against UPS_VCELL_MIN_STOP. If cell voltage exceeds UPS_VCELL_MIN_STOP the timer is rearmed so it can filter unwanted noise in the system.\r\nDefault is 5 sec."), _T("[s]")},
42 | {14, 2, _T("UPS_HARDOFF_TOUT"), true, 1, 1, 0, 0, _T("After UPS_VBAT_UVP_OFF_TOUT passed motherboard is signaled to shut down. The UPS will wait this period so that the Motherboard can shut down gracefully, than cut power and enter deep sleep. This period should not be too long to prevent battery drain.\r\nDefault is 60 sec."), _T("[s]")},
43 |
44 | {101, 2, _T("UPS_VBAT_SLEEP_TOUT"), true, 3, 1, 0, 0, _T("Deep sleep timeout..If system is running on battery and this period has elapsed with no input power applied, then the UPS will initiate shut down procedure. \"Never\" is allowed as value (to disable this feature).\r\nDefault is \"Never\"."), _T("[HH:MM:SS]")},
45 |
46 | //moved up {16, 2, _T("UPS_VCELL_MIN_HARD_STOP") , true,10, 6, 0, 0 , _T("If VIN is not present and one of the battery cells is less than this threshold the UPS will instantly shut down to preserve battery.\r\nDefault is 2V "), _T("[V]") },
47 | // {18, 2, _T("UPS_SWITCHOVER_VBAT_TOUT") , true, 1, 10, 0, 0 , _T("After UPS switches to battery, can switch back to Vin only after this time ellapses\r\nDefault is 1000 ms"), _T("[ms]") },
48 | // {20, 2, _T("UPS_SWITCHOVER_VIN_TOUT") , true, 1, 1, 0, 0 , _T("After UPS switches to input, can switch back to battery only after this time ellapses\r\nDefault is 1 ms"), _T("[ms]") },
49 | {22, 2, _T("DCHG_IMAX"), true, 10, 5, 0, 0, _T("Max allowed discharge current. In case discharge current exceeds this threshold shutdown will be initiated.\r\nDefault is 10 A."), _T("[A]")},
50 | {24, 1, _T("AFE_OLV"), true, 40, 1, 0, 0, _T("Advanced parameter.\r\nAFE Overload Voltage treshold configuration register.\r\nDefault is 00."), _T("[hex]")},
51 | {25, 1, _T("AFE_OLD"), true, 40, 1, 0, 0, _T("Advanced parameter.\r\nAFE Overload Delay time configuration register.\r\nDefault is 00."), _T("[hex]")},
52 | {26, 1, _T("AFE_SCC"), true, 40, 1, 0, 0, _T("Advanced parameter.\r\nAFE Short Circuit In Charge configuration register.\r\nDefault is 50."), _T("[hex]")},
53 | {27, 1, _T("AFE_SCD"), true, 40, 1, 0, 0, _T("Advanced parameter.\r\nAFE Short Circuit In Discharge configuration register.\r\nDefault is 50."), _T("[hex]")},
54 | // moved up {28, 4, _T("CAPACITY") , true,16, 1, 0, 0 , _T("Battery Capacity.\r\nDefault 3400mAh"), _T("[mAh]") },
55 |
56 | //moved down {32, 2, _T("BAL_ENABLE_TOUT") , true,1, 5, 0, 0 , _T("Balancing enable timeout.\r\nDefault is 2 min."), _T("[min]") },
57 | //moved down {34, 2, _T("BAL_DISABLE_TOUT") , true,1, 5, 0, 0 , _T("Balancing disable timeout.\r\nDefault is 2 min."), _T("[min]") },
58 | {36, 2, _T("CHG_VCOND"), true, 10, 6, 0, 0, _T("Conditioning/Pre-charge voltage. Charge current is limited until cell voltage exceeds this value and for at least CHG_TCOND time.\r\nDefault is 3V."), _T("[V]")},
59 | {38, 2, _T("CHG_TCOND"), true, 1, 1, 0, 0, _T("Conditioning/Precharge time. Charge current is limited until cell voltage exceeds CHG_VCOND value and it is applied during this time.\r\nDefault is 30 sec."), _T("[s]")},
60 | // {40, 2, _T("CHG_IBULK") , true,10, 3, 0, 0 , _T("Fast charge current limit (constant current mode)\r\nDefault is 1750mA."), _T("[mA]") },
61 | {42, 2, _T("CHG_BULK_STOP_VOLTAGE"), true, 10, 6, 0, 0, _T("Maximum allowed bulk charge voltage/cell during constant current/constant voltage charging\r\nDefault is 3.5[V/cell]"), _T("[V/cell]")},
62 | {71, 1, _T("CHG_HYSTERESIS"), true, 10, 6, 0, 0, _T("An over-voltage value (CHG_BULK_STOP_VOLTAGE+CHG_HYSTERESIS ) that it is allowed when charging.\r\nIf one of the cells exceeds this value charging is immediately stopped.\r\nDefault is 100mV/cell."), _T("[V/cell]")},
63 | //moved down {44, 2, _T("OUT_FOLLOW_OFFSET") , true,30, 10, 0, 0 , _T("-"), _T("[V]") },
64 | // {46, 2, _T("CHG_IMIN") , true,10, 3, 0, 0 , _T("If charge current in CV mode is less than this value\r\nPbSO4: enter maintanence mode and apply the float charge voltage(CHG_START_VOLTAGE)\r\nLiFePO4: cut off the charge voltage\r\nDefault is 290mA"), _T("[mA]") },
65 |
66 | {96, 2, _T("CHG_GLOBAL_TOUT"), true, 1, 0.5, 0, 0, _T("Global charge timeout.\r\nDefault is 180 min."), _T("[min]")},
67 | // {98, 2, _T("CHG_IFLOAT") , true,10, 3, 0, 0 , _T("Charge current limit in float charge mode for PbSO4 batteries (Firmware v1.3+)"), _T("[mA]") },
68 | {94, 2, _T("CHG_TOPPING_TIMER"), true, 1, 1, 0, 0, _T("For Lithium based batteries after an overvoltage condition is detected for a cell a resting period is set by this timer before applying a small topping charge in case other cells are still not charged.\r\nDefault is 18000s."), _T("[s]")},
69 |
70 | {48, 2, _T("CHG_START_VOLTAGE"), true, 10, 6, 0, 0, _T("If cell voltage is below this value charging can be started.\r\nDefault is 3.32V/cell"), _T("[V/cell]")},
71 | {50, 1, _T("CHG_BAT_TYPE"), true, 1, 1, 0, 0, _T("The battery chemistry to be charged.\r\n1-LiFePO4"), _T("")},
72 | {51, 2, _T("CHG_TEMP_COLD"), true, 15, 1, 0, 0, _T("COLD temperature treshold for temperature compensated charge current regulation\r\nDefault 5�C"), _T("[�C]")},
73 | {53, 2, _T("CHG_TEMP_COOL"), true, 15, 1, 0, 0, _T("COOL temperature treshold for temperature compensated charge current regulation\r\nDefault 10�C"), _T("[�C]")},
74 | {55, 2, _T("CHG_TEMP_WARM"), true, 15, 1, 0, 0, _T("WARM temperature treshold for temperature compensated charge current regulation\r\nDefault 50�C"), _T("[�C]")},
75 | {57, 2, _T("CHG_TEMP_HOT"), true, 15, 1, 0, 0, _T("HOT temperature treshold for temperature compensated charge current regulation\r\nDefault 55�C"), _T("[�C]")},
76 | //moved down {59, 1, _T("OUT_STARTVOLTAGE_OFFSET") , true, 1, 1, 0, 0 , _T("Soft start output voltage offset"), _T("[nr]") },
77 |
78 | {60, 2, _T("BAL_VCELL_MIN"), true, 10, 6, 0, 0, _T("Balancing is allowed if cell voltages are above this value.\r\nDefault is 3.32V"), _T("[V]")},
79 | {62, 1, _T("BAL_VCELL_DIFF_START"), true, 10, 6, 0, 0, _T("If the voltage difference between cells exceeds this value start balancing the cells.\r\nDefault is 70mV."), _T("[V]")},
80 | {63, 1, _T("BAL_VCELL_DIFF_STOP"), true, 10, 6, 0, 0, _T("If the voltage difference between cells is less than this value stop balancing the cells.\r\nDefault is 40mV."), _T("[V]")},
81 | /* {32, 2, _T("BAL_ENABLE_TOUT"), true, 1, 5, 0, 0, _T("Balancing enable timeout (FW 1.5+)\r\nDefault 72000 min."), _T("[min]")}, */
82 | /* {34, 2, _T("BAL_DISABLE_TOUT"), true, 1, 5, 0, 0, _T("Balancing disable timeout (FW 1.5+)\r\nDefault 1440 min."), _T("[min]")}, */
83 |
84 | {64, 2, _T("OUT_VOLTAGE"), true, /*7*/ 10, 1, 0, 0, _T("The output voltage. Bit0 in UPS_CONFIG parameter should be set to �1� in order this parameter has effect.\r\nDefault is 12V."), _T("[V]")},
85 | /**/ {59, 1, _T("OUT_STARTVOLTAGE_OFFSET"), true, 1, 1, 0, 0, _T("Soft start output voltage offset"), _T("[nr]")},
86 | //moved up {66, 1, _T("UPS_BUTTON_ON_TOUT") , true, 1, 10, 0, 0 , _T(""), _T("[]") },
87 | //moved up {67, 1, _T("OPENUPSMODE") , true,17, 1, 0, 0 , _T("OpenUPS Mode\r\n0-Auto restart when Input Power is Present=YES\r\n1-Auto restart when Input Power is Present=NO"), _T("[-]") },
88 | // {68, 1, _T("OUT_REGULATOR_OFFSET") , true, 1, 1, 0, 0 , _T("Regulator offset"), _T("[step]") },
89 | {69, 1, _T("OUT_MAX_REGULATOR_STEP"), true, 1, 1, 0, 0, _T("Maximum allowed regulation step number for output module (0-255)\r\nDefault 255."), _T("[step]")},
90 | //removed {70, 1, _T("CELLS") , true, 1, 1, 0, 0 , _T("Number of configured cells to charge/balance\r\nDefault is 4."), _T("[pcs]") },
91 | //moved up {71, 1, _T("CHG_HYSTERESIS") , true,10, 2, 0, 0 , _T("An overvoltage value (CHG_BULK_STOP_VOLTAGE +CHG_HYSTERESIS ) that it is allowed when charging.If one of the cells exceeds this value charging is imediately stopped.\r\nDefault is 200mV/cell."), _T("[V]") },
92 | //removed {72, 4, _T("POUT_LO") , true,16, 0.000001, 0, 0 , _T("Output power low treshold for motherboard alive sensing.If output power is lower than this treshold shut down impulse is NOT sent to the motherboard.\r\nDefault is 2W."), _T("[W]") },
93 | //removed {76, 4, _T("POUT_HI") , true,16, 0.000001, 0, 0 , _T("Output power high treshold for motherboard alive sensing. If output power is higher than this treshold shut down impulse is sent to the motherboard.\r\nDefault is 6W."), _T("[W]") },
94 | //removed {44, 2, _T("OUT_FOLLOW_OFFSET") , true,30, 10, 0, 0 , _T("Offset for adaptive output regulation.\r\nDefault is 1V."), _T("[V]") },
95 |
96 | //moved up {80, 2, _T("MOB_ONOFF_TOUT") , true, 1, 10, 0, 0 , _T("The powerswitch on the motherboard(if connected) will be shorted this period to turn the Motherboard ON or OFF\r\nDefault is 500ms"), _T("[mS]") },
97 | {82, 2, _T("OCV_SOC0"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 0% fuel gauge estimation.\r\nDefault is 3.1V"), _T("[V]")},
98 | {84, 2, _T("OCV_SOC10"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 10% fuel gauge estimation.\r\nDefault is 3.2V"), _T("[V]")},
99 | {86, 2, _T("OCV_SOC25"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 25% fuel gauge estimation.\r\nDefault is 3.25V"), _T("[V]")},
100 | {88, 2, _T("OCV_SOC50"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 50% fuel gauge estimation.\r\nDefault is 3.30V"), _T("[V]")},
101 | {90, 2, _T("OCV_SOC75"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 75% fuel gauge estimation.\r\nDefault is 3.32V"), _T("[V]")},
102 | {92, 2, _T("OCV_SOC100"), true, 10, 6, 0, 0, _T("Open Circuit Voltage State Of Charge detection for initial 100% fuel gauge estimation.\r\nDefault is 3.35V"), _T("[V]")},
103 | //moved up {94, 2, _T("CHG_TOPPING_TIMER") , true, 1, 1, 0, 0 , _T("For Lithium based batteries after an overvoltage condition is detected for a cell a resting period is set by this timer before applying a small topping charge in case other cells are still not charged.\r\nDefault is 1800 sec."), _T("[s]") },
104 |
105 | //moved up {96, 2, _T("CHG_GLOBAL_TOUT") , true, 1, 0.5, 0, 0 , _T("Global charge timeout (Firmware v1.3+)"), _T("[min]") },
106 | //deleted {98, 2, _T("CHG_IFLOAT") , true,10, 3, 0, 0 , _T("Charge current limit in float charge mode (Firmware v1.3+)"), _T("[mA]") },
107 | //moved up {100,1, _T("UPS_CONFIG2") , true,14, 1, 0, 0 , _T("Configuration register (Firmware V1.4+). Used for enabling disabling modules. LSB bit is b0.\r\nbit0: if bit is set and running on battery and measured output power> 4) & 0x0F;
216 | m_nVerMinor = msg[31] & 0x0F;
217 |
218 | m_fVIN = convertOneValue2Float(msg, 2, 1, 20, HIDOpenUPS2::GetConstant(0));
219 | m_fVOut = convertOneValue2Float(msg, 2, 3, 20, HIDOpenUPS2::GetConstant(1));
220 | m_fVBat = convertOneValue2Float(msg, 2, 5, 20, HIDOpenUPS2::GetConstant(6));
221 |
222 | for (i = 0; i < 3; i++)
223 | m_fVCell[i] = convertOneValue2Float(msg, 2, 7 + 2 * i, 20, HIDOpenUPS2::GetConstant(6));
224 |
225 | for (i = 0; i < 8; i++)
226 | {
227 | m_bCheck23[i] = ((msg[23] >> i) & 1);
228 | m_bCheck24[i] = ((msg[24] >> i) & 1);
229 | m_bCheck25[i] = ((msg[25] >> i) & 1);
230 | }
231 |
232 | m_fCCharge = convertOneValue2Float(msg, 2, 19, 2, (double)0.001);
233 | m_fCDischarge = convertOneValue2Float(msg, 2, 21, 2, (double)0.001);
234 |
235 | if (m_bCheck24[6])
236 | m_nState = 1;
237 | else if (m_bCheck24[5])
238 | m_nState = 2;
239 | else
240 | m_nState = 3; //only usb
241 |
242 | m_fTemperature[0] = convertOneValue2Float(msg, 2, 13, 15, 1);
243 | m_fTemperature[1] = convertOneValue2Float(msg, 2, 15, 15, 1);
244 | m_fTemperature[2] = convertOneValue2Float(msg, 2, 17, 15, 1);
245 |
246 | m_fVDuty = convertOneValue2Float(msg, 2, 26, 20, (double)0.0009765625);
247 |
248 | m_bVOutPot = 255 - msg[28];
249 | m_bConfigSwitch = msg[29];
250 | }
251 | break;
252 | case OPENUPS2_CLOCK_IN:
253 | {
254 | int i;
255 |
256 | if ((m_nVerMajor >= 1) && (m_nVerMinor >= 4))
257 | {
258 | m_nShutdownType = msg[7];
259 | m_nShutdownType = (m_nShutdownType << 8) | msg[6];
260 | }
261 | else
262 | m_nShutdownType = 0;
263 |
264 | m_bStateUPS = msg[9];
265 | m_bStateCHG = msg[10];
266 | m_bStateDBG = msg[11];
267 | m_bCapacity = msg[12];
268 |
269 | for (i = 0; i < 3; i++)
270 | m_bBatOn[i] = ((msg[15] >> i) & 1);
271 |
272 | m_nRTE = msg[17];
273 | m_nRTE = (m_nRTE << 8) | msg[16];
274 |
275 | m_bUPSMode = msg[25];
276 |
277 | for (i = 0; i < 4; i++)
278 | m_bDBG[i] = msg[28 + i];
279 | }
280 | break;
281 | case OPENUPS2_MEM_READ_IN:
282 | {
283 | memcpy(m_chPackages + (m_ulSettingsAddr - OPENUPS2_SETTINGS_ADDR_START), msg + 5, 16);
284 | /*
285 | fprintf(stderr, "%02lx %02lx", m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF);
286 | for (int len = 0; len < 16; len++)
287 | fprintf(stderr, " %02x", msg[5 + len]);
288 | fprintf(stderr, "\n");
289 | */
290 | }
291 | break;
292 | }
293 | }
294 |
295 | float HIDOpenUPS2::convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier)
296 | {
297 | double value = -1;
298 |
299 | if (nLen == 1)
300 | value = (double)buffer[nIndex] * dMultiplier;
301 | else if (nLen == 2)
302 | value = (double)((((int)buffer[nIndex + 1]) << 8) | buffer[nIndex]) * dMultiplier;
303 | else if (nLen == 4)
304 | value = (double)((((int)buffer[nIndex + 3]) << 24) | (((int)buffer[nIndex + 2]) << 16) | (((int)buffer[nIndex + 1]) << 8) | buffer[nIndex]) * dMultiplier;
305 |
306 | switch (nReadMode)
307 | {
308 | case 10:
309 | {
310 | if (nLen == 1)
311 | value = (double)buffer[nIndex] * HIDOpenUPS2::GetConstant((int)dMultiplier);
312 | else
313 | value = (double)((((int)buffer[nIndex + 1]) << 8) | buffer[nIndex]) * HIDOpenUPS2::GetConstant((int)dMultiplier);
314 | }
315 | break;
316 | case 30:
317 | {
318 | if (nLen == 1)
319 | value = (double)buffer[nIndex] * HIDOpenUPS2::GetConstant(6);
320 | else
321 | value = (double)((((int)buffer[nIndex + 1]) << 8) | buffer[nIndex]) * HIDOpenUPS2::GetConstant(6);
322 | }
323 | break;
324 | case 15:
325 | {
326 | unsigned int termistor = 0;
327 | termistor = buffer[nIndex + 1];
328 | termistor = (termistor << 8) | buffer[nIndex];
329 |
330 | if (termistor <= HIDOpenUPS2::GetTermistorConsts()[0])
331 | value = -40;
332 | else if (termistor >= HIDOpenUPS2::GetTermistorConsts()[TERMISTOR_CONSTS - 1])
333 | value = 125;
334 | else
335 | {
336 | int pos = -1;
337 | for (int i = TERMISTOR_CONSTS - 1; i >= 0; i--)
338 | {
339 | if (termistor >= HIDOpenUPS2::GetTermistorConsts()[i])
340 | {
341 | pos = i;
342 | break;
343 | }
344 | }
345 |
346 | //if (termistor != HIDOpenUPS2::GetTermistorConsts()[i]) pos--;//latest value which is smaller than my one
347 |
348 | if (termistor == HIDOpenUPS2::GetTermistorConsts()[pos])
349 | value = pos * 5 - 40;
350 | else
351 | {
352 | int t1 = pos * 5 - 40;
353 | int t2 = (pos + 1) * 5 - 40;
354 |
355 | unsigned int d1 = HIDOpenUPS2::GetTermistorConsts()[pos];
356 | unsigned int d2 = HIDOpenUPS2::GetTermistorConsts()[pos + 1];
357 |
358 | float dtemp = ((float)termistor - (float)d1) * ((float)t2 - (float)t1) / ((float)d2 - (float)d1);
359 |
360 | int temp = (int)ceil(dtemp) + t1;
361 |
362 | value = temp;
363 | }
364 | }
365 | }
366 | break;
367 | }
368 |
369 | //DBGOutput("convertOneValue2Float %.3f\n",value);
370 |
371 | return (float)value;
372 | }
373 |
374 | bool HIDOpenUPS2::readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4)
375 | {
376 | bool ok = false;
377 | c1 = 0;
378 | c2 = 0;
379 |
380 | double val = 0;
381 |
382 | switch (nReadMode)
383 | {
384 | case 3:
385 | { //hh:mm:ss
386 | if (_strcmpi(str, "Never") == 0)
387 | {
388 | val = 0xFFFF;
389 | ok = true;
390 | }
391 | else if ((strlen(str) == 8) && (str[2] == ':') && (str[5] == ':'))
392 | {
393 | _TCHAR *end = NULL;
394 | char temps[10];
395 |
396 | str_left(temps, str, 2);
397 | int hh = _tcstol(temps, &end, 10);
398 | if (((end != str) && (hh >= 0) && (hh < 18)))
399 | {
400 | str_mid(temps, str, 3, 2);
401 | int mm = _tcstol(temps, &end, 10);
402 | if ((end != str) && (mm >= 0) && (mm < 60))
403 | {
404 | str_right(temps, str, 2);
405 | int ss = _tcstol(temps, &end, 10);
406 |
407 | if (((end != str) && (ss >= 0) && (ss < 60)))
408 | {
409 | val = hh * 3600 + mm * 60 + ss;
410 | ok = true;
411 | }
412 | }
413 | }
414 | }
415 | }
416 | break;
417 | case 4:
418 | { //"signed" float
419 | if (strlen(str) != 0)
420 | {
421 | bool negative = false;
422 | if (str[0] == '-')
423 | {
424 | str = str + 1;
425 | negative = true;
426 | }
427 |
428 | _TCHAR *end = NULL;
429 | val = _tcstod(str, &end);
430 |
431 | if (end != str)
432 | {
433 | ok = true;
434 |
435 | val /= dMultiplier;
436 | if (val < 0)
437 | val = 0;
438 | double rem = val - (int)val;
439 | unsigned long uval = (unsigned long)val;
440 | if (rem >= 0.5)
441 | uval += 1;
442 |
443 | if (uval > 0x7F)
444 | uval = 0x7F;
445 |
446 | c1 = 0;
447 | c2 = (unsigned char)uval;
448 | if (negative)
449 | c2 |= 0x80;
450 | }
451 | }
452 | }
453 | break;
454 | case 6:
455 | {
456 | val = 0xFF;
457 | ok = true;
458 | }
459 | break;
460 | case 7:
461 | {
462 | /*_TCHAR* end = NULL;
463 | val = _tcstod(str, &end);
464 |
465 | c2 = 0;
466 | c1 = HIDOpenUPS2::GetDataVOut(val);
467 | ok = true;*/
468 | }
469 | break;
470 | case 8:
471 | {
472 | if (_strcmpi(str, "Never") == 0)
473 | {
474 | val = (double)0xFFFF * dMultiplier;
475 | ok = true;
476 | }
477 | else
478 | {
479 | TCHAR *end = NULL;
480 | val = _tcstod(str, &end);
481 | if (end != str)
482 | ok = true;
483 | }
484 | }
485 | break;
486 | case 12:
487 | case 13:
488 | {
489 | c1 = 0;
490 | c2 = atoi(str);
491 | ok = true;
492 | }
493 | break;
494 | case 14:
495 | {
496 | c1 = 0;
497 | c2 = bin2char(str, &ok);
498 | }
499 | break;
500 | case 15:
501 | {
502 | unsigned int termistor = 0;
503 |
504 | int temp = atoi(str);
505 | if (temp <= -40)
506 | termistor = HIDOpenUPS2::GetTermistorConsts()[0];
507 | else if (temp >= 125)
508 | termistor = HIDOpenUPS2::GetTermistorConsts()[TERMISTOR_CONSTS - 1];
509 | else
510 | {
511 | int pos = (temp + 40) / 5;
512 | int temp_dif = (temp + 40) % 5;
513 |
514 | if (temp_dif == 0)
515 | termistor = HIDOpenUPS2::GetTermistorConsts()[pos];
516 | else
517 | {
518 | unsigned int d1 = HIDOpenUPS2::GetTermistorConsts()[pos];
519 | unsigned int d2 = HIDOpenUPS2::GetTermistorConsts()[pos + 1];
520 |
521 | termistor = d1 + temp_dif * (d2 - d1) / 5;
522 | }
523 | }
524 |
525 | c1 = (unsigned char)(((unsigned int)termistor) & 0xFF);
526 | c2 = (unsigned char)((((unsigned int)termistor) >> 8) & 0xFF);
527 | ok = true;
528 | }
529 | break;
530 | case 16:
531 | {
532 | TCHAR *end = NULL;
533 | val = _tcstod(str, &end);
534 |
535 | if ((val != 0) && (end != str))
536 | {
537 | val = ceil(val / dMultiplier);
538 | unsigned int ival = (unsigned int)val;
539 |
540 | c1 = (unsigned char)(ival & 0xFF);
541 | c2 = (unsigned char)((ival >> 8) & 0xFF);
542 | c3 = (unsigned char)((ival >> 16) & 0xFF);
543 | c4 = (unsigned char)((ival >> 24) & 0xFF);
544 | ok = true;
545 | }
546 | }
547 | break;
548 | case 17:
549 | {
550 | _TCHAR *end = NULL;
551 | val = _tcstod(str, &end);
552 | if (end != str)
553 | ok = true;
554 | if ((val < 0) || (val > 255))
555 | ok = false;
556 | }
557 | break;
558 | case 40:
559 | {
560 | _TCHAR *end = NULL;
561 | val = _tcstol(str, &end, 16);
562 | ok = true;
563 | }
564 | break; //hex
565 | case 1:
566 | //normal read - integer
567 | case 2:
568 | case 9:
569 | case 5:
570 | default:
571 | { //normal read - float
572 | _TCHAR *end = NULL;
573 | val = _tcstod(str, &end);
574 |
575 | if (end != str)
576 | ok = true;
577 | }
578 | }
579 |
580 | if (ok)
581 | {
582 | if ((nReadMode != 4) && (nReadMode != 7) && (nReadMode != 9) && (nReadMode != 11) && (nReadMode != 12) && (nReadMode != 13) && (nReadMode != 14) && (nReadMode != 15) && (nReadMode != 16))
583 | {
584 | switch (nReadMode)
585 | {
586 | case 10:
587 | val /= HIDOpenUPS2::GetConstant((int)dMultiplier);
588 | break;
589 | case 30:
590 | val /= HIDOpenUPS2::GetConstant(6);
591 | break;
592 | default:
593 | val /= dMultiplier;
594 | }
595 |
596 | if (nReadMode != 5)
597 | { //unsigned
598 | if (val < 0)
599 | val = 0;
600 | double rem = val - (int)val;
601 | unsigned long uval = (unsigned long)val;
602 | if (rem >= 0.5)
603 | uval += 1;
604 |
605 | if (len == 1)
606 | {
607 | if (uval > 0xFF)
608 | uval = 0xFF;
609 |
610 | c1 = 0;
611 | c2 = (unsigned char)uval;
612 | }
613 | else
614 | {
615 | if (uval > 0xFFFF)
616 | uval = 0xFFFF;
617 |
618 | c1 = (unsigned char)(((unsigned int)uval) & 0xFF);
619 | c2 = (unsigned char)((((unsigned int)uval) >> 8) & 0xFF);
620 | }
621 |
622 | //DBGOutput(" uval1=%d %x\n",uval,uval);
623 | }
624 | else
625 | {
626 | double rem = -val + (int)val;
627 | long uval = (long)val;
628 | if (rem >= 0.5)
629 | uval += 1;
630 |
631 | if (len == 1)
632 | {
633 | if (uval > 127)
634 | uval = 127;
635 | if (uval < -128)
636 | uval = -128;
637 |
638 | c1 = 0;
639 | c2 = (char)uval;
640 | }
641 | else
642 | {
643 | if (uval > 32767)
644 | uval = 32767;
645 | if (uval < -32768)
646 | uval = -32768;
647 |
648 | c1 = (char)(((int)uval) & 0xFF);
649 | c2 = (char)((((int)uval) >> 8) & 0xFF);
650 | }
651 | //DBGOutput(" uval2=%d %x\n",uval,uval);
652 | }
653 | }
654 | }
655 |
656 | //DBGOutput("readOneValue %f [%02x][%02x]\n",(float)val,(unsigned int)c2,(unsigned int)c1);
657 |
658 | return ok;
659 | }
660 |
661 | void HIDOpenUPS2::convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier)
662 | {
663 | destination[0] = 0; //empty string
664 | double value = 0;
665 |
666 | if (nLen == 1)
667 | value = (double)m_chPackages[nIndex] * dMultiplier;
668 | else if (nLen == 2)
669 | value = (double)((((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * dMultiplier;
670 | else if (nLen == 4)
671 | value = (double)((((int)m_chPackages[nIndex + 3]) << 24) | (((int)m_chPackages[nIndex + 2]) << 16) | (((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * dMultiplier;
672 |
673 | switch (nReadMode)
674 | {
675 | case 2:
676 | case 9:
677 | sprintf(destination, "%.3f", (float)value);
678 | break;
679 | case 20:
680 | sprintf(destination, "%.2f", (float)value);
681 | break;
682 | case 10:
683 | {
684 | if (nLen == 1)
685 | value = (double)m_chPackages[nIndex] * HIDOpenUPS2::GetConstant((int)dMultiplier);
686 | else if (nLen == 2)
687 | value = (double)((((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * HIDOpenUPS2::GetConstant((int)dMultiplier);
688 | else if (nLen == 4)
689 | value = (double)((((int)m_chPackages[nIndex + 3]) << 24) | (((int)m_chPackages[nIndex + 2]) << 16) | (((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * HIDOpenUPS2::GetConstant((int)dMultiplier);
690 | sprintf(destination, "%.3f", (float)value);
691 | }
692 | break;
693 | case 30:
694 | {
695 | if (nLen == 1)
696 | value = (double)m_chPackages[nIndex] * HIDOpenUPS2::GetConstant(6);
697 | else if (nLen == 2)
698 | value = (double)((((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * HIDOpenUPS2::GetConstant(6);
699 | else if (nLen == 4)
700 | value = (double)((((int)m_chPackages[nIndex + 3]) << 24) | (((int)m_chPackages[nIndex + 2]) << 16) | (((int)m_chPackages[nIndex + 1]) << 8) | m_chPackages[nIndex]) * HIDOpenUPS2::GetConstant(6);
701 | if (value > dMultiplier)
702 | value = dMultiplier;
703 | sprintf(destination, "%.3f", (float)value);
704 | }
705 | break;
706 | case 12:
707 | case 13:
708 | sprintf(destination, "%d", m_chPackages[nIndex]);
709 | break;
710 | case 14:
711 | char2bin(destination, m_chPackages[nIndex]);
712 | break;
713 | case 15:
714 | {
715 | unsigned int termistor = 0;
716 | termistor = m_chPackages[nIndex + 1];
717 | termistor = (termistor << 8) | m_chPackages[nIndex];
718 |
719 | if (termistor <= HIDOpenUPS2::GetTermistorConsts()[0])
720 | strcpy(destination, "-40");
721 | else if (termistor >= HIDOpenUPS2::GetTermistorConsts()[TERMISTOR_CONSTS - 1])
722 | strcpy(destination, "125");
723 | else
724 | {
725 | int pos = -1;
726 | for (int i = TERMISTOR_CONSTS - 1; i >= 0; i--)
727 | {
728 | if (termistor >= HIDOpenUPS2::GetTermistorConsts()[i])
729 | {
730 | pos = i;
731 | break;
732 | }
733 | }
734 |
735 | //if (termistor != HIDOpenUPS2::GetTermistorConsts()[i]) pos--;//latest value which is smaller than my one
736 | if (termistor == HIDOpenUPS2::GetTermistorConsts()[pos])
737 | sprintf(destination, "%d", pos * 5 - 40);
738 | else
739 | {
740 | int t1 = pos * 5 - 40;
741 | int t2 = (pos + 1) * 5 - 40;
742 |
743 | unsigned int d1 = HIDOpenUPS2::GetTermistorConsts()[pos];
744 | unsigned int d2 = HIDOpenUPS2::GetTermistorConsts()[pos + 1];
745 |
746 | float dtemp = ((float)termistor - (float)d1) * ((float)t2 - (float)t1) / ((float)d2 - (float)d1);
747 |
748 | int temp = (int)ceil(dtemp) + t1;
749 |
750 | sprintf(destination, "%d", temp);
751 | }
752 | }
753 | }
754 | break;
755 | case 16:
756 | { //4 byte value
757 | if (dMultiplier == 1)
758 | sprintf(destination, "%.0f", (float)value);
759 | else
760 | sprintf(destination, "%.2f", (float)value);
761 | }
762 | break;
763 | case 17:
764 | {
765 | sprintf(destination, "%d", (int)value);
766 | }
767 | break;
768 | case 3:
769 | {
770 | unsigned int ivalue = (unsigned int)value;
771 | if (ivalue == 0xFFFF)
772 | strcpy(destination, "Never");
773 | else
774 | sprintf(destination, "%02d:%02d:%02d", ivalue / 3600, (ivalue / 60) % 60, ivalue % 60);
775 | }
776 | break;
777 | case 4:
778 | {
779 | double dvalue = (m_chPackages[nIndex] & 0x7f) * dMultiplier;
780 | if ((m_chPackages[nIndex] & 0x80) != 0)
781 | sprintf(destination, "-%.3f", (float)value);
782 | else
783 | sprintf(destination, "%.3f", (float)value);
784 | }
785 | break;
786 | case 5:
787 | {
788 | sprintf(destination, "%d", (int)value);
789 | }
790 | break;
791 | case 6:
792 | {
793 | strcpy(destination, "-");
794 | }
795 | break;
796 | case 7:
797 | { //no multiplier - voltage value calcualted with formula
798 | //! sprintf(destination, "%.3f", HIDOpenUPS2::GetVoltageVOut(m_chPackages[nIndex]));
799 | }
800 | break;
801 | case 8:
802 | {
803 | if (nLen == 1)
804 | {
805 | if (m_chPackages[nIndex] == 0xFF)
806 | strcpy(destination, "Never");
807 | else
808 | sprintf(destination, "%d", (int)value);
809 | }
810 | else
811 | {
812 | if ((m_chPackages[nIndex] == 0xFF) && (m_chPackages[nIndex + 1] == 0xFF))
813 | strcpy(destination, "Never");
814 | else
815 | sprintf(destination, "%d", (int)value);
816 | }
817 | }
818 | break;
819 | case 40: //hex
820 | if (nLen == 1)
821 | sprintf(destination, "%02x", (int)value);
822 | else if (nLen == 2)
823 | sprintf(destination, "%04x", (int)value);
824 | else if (nLen == 4)
825 | sprintf(destination, "%08x", (int)value);
826 | break;
827 | case 1:
828 | default:
829 | {
830 | sprintf(destination, "%d", (int)value);
831 | }
832 | }
833 |
834 | //DBGOutput("convertOneValue2String %d %s %f [%x][%x]\n",nIndex,destination,(float)value,(int)m_chPackages[nIndex],(int)m_chPackages[nIndex+1]);
835 | }
836 |
837 | bool HIDOpenUPS2::setVariableData(int mesg_no, char *str)
838 | {
839 | int len = HIDOpenUPS2::GetMessages()[mesg_no].nLen;
840 |
841 | if (len != 0)
842 | {
843 | unsigned char c1 = 0;
844 | unsigned char c2 = 0;
845 | unsigned char c3 = 0;
846 | unsigned char c4 = 0;
847 |
848 | if (readOneValue(str, HIDOpenUPS2::GetMessages()[mesg_no].nReadMode, HIDOpenUPS2::GetMessages()[mesg_no].dMultiplier, len, c1, c2, c3, c4))
849 | {
850 | switch (len)
851 | {
852 | case 1:
853 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex] = c2;
854 | break;
855 | case 2:
856 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex] = c1;
857 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex + 1] = c2;
858 | break;
859 | case 4:
860 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex] = c1;
861 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex + 1] = c2;
862 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex + 2] = c3;
863 | m_chPackages[HIDOpenUPS2::GetMessages()[mesg_no].nIndex + 3] = c4;
864 | break;
865 | }
866 | return 1;
867 | }
868 | else
869 | return 0;
870 | }
871 | return 1;
872 | }
873 |
874 | unsigned char HIDOpenUPS2::getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment)
875 | {
876 | if ((cnt < 0) || (cnt >= MAX_MESSAGE_CNT))
877 | return 0;
878 |
879 | if (HIDOpenUPS2::GetMessages()[cnt].nLen == 0)
880 | return 0;
881 |
882 | if (name)
883 | strcpy(name, HIDOpenUPS2::GetMessages()[cnt].strName);
884 | if (unit)
885 | strcpy(unit, HIDOpenUPS2::GetMessages()[cnt].strUnit);
886 | if (comment)
887 | strcpy(comment, HIDOpenUPS2::GetMessages()[cnt].strText);
888 |
889 | if (value)
890 | {
891 | convertOneValue2String(value, HIDOpenUPS2::GetMessages()[cnt].nLen,
892 | HIDOpenUPS2::GetMessages()[cnt].nIndex,
893 | HIDOpenUPS2::GetMessages()[cnt].nReadMode,
894 | HIDOpenUPS2::GetMessages()[cnt].dMultiplier);
895 | }
896 |
897 | return HIDOpenUPS2::GetMessages()[cnt].nReadMode;
898 | }
899 |
900 | void HIDOpenUPS2::restartUPS()
901 | {
902 | sendCommand(OPENUPS2_DCMD_RESTART_UPS, 1);
903 | }
904 |
905 | void HIDOpenUPS2::restartUPSInBootloaderMode()
906 | {
907 | sendMessage(OPENUPS2_ENTER_BOOTLOADER_OUT, 2, 0xFA, 0xCD);
908 | }
909 |
910 | void HIDOpenUPS2::setVOutVolatile(float vout)
911 | {
912 | int voltage = (int)(vout * 1000.0);//mV
913 | sendCommandEx(OPENUPS2_DCMD_SPI_WRITE_DATA_VOL_VOUT, (voltage >> 8) & 0xFF, voltage & 0xFF);
914 | }
915 |
916 | void HIDOpenUPS2::incDecVOutVolatile(unsigned char inc)
917 | {
918 | if (inc) // wiper is opposite to vout
919 | sendCommand(OPENUPS2_DCMD_SPI_DEC_VOL_WIPER_VOUT, 0);
920 | else
921 | sendCommand(OPENUPS2_DCMD_SPI_INC_VOL_WIPER_VOUT, 0);
922 | }
923 |
924 | void HIDOpenUPS2::GetStatus()
925 | {
926 | unsigned char recv[32];
927 | int ret;
928 |
929 | sendMessage(OPENUPS2_GET_ALL_VALUES, 0);
930 | recvMessage(recv);
931 | parseMessage(recv);
932 |
933 | ret = sendMessage(OPENUPS2_GET_ALL_VALUES_2, 0);
934 | recvMessage(recv);
935 | parseMessage(recv);
936 |
937 | ret = sendMessage(OPENUPS2_GET_ALL_VALUES_3, 0);
938 | recvMessage(recv);
939 | parseMessage(recv);
940 |
941 | ret = sendMessage(OPENUPS2_CLOCK_OUT, 0);
942 | recvMessage(recv);
943 | parseMessage(recv);
944 | }
945 |
946 | void HIDOpenUPS2::ReadConfigurationMemory()
947 | {
948 | unsigned char recv[32];
949 |
950 | m_ulSettingsAddr = OPENUPS2_SETTINGS_ADDR_START;
951 | memset(m_chPackages, 0, SETTINGS_PACKS * 16);
952 |
953 | while (m_ulSettingsAddr < OPENUPS2_SETTINGS_ADDR_END)
954 | {
955 | sendMessage(OPENUPS2_MEM_READ_OUT, 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x10);
956 | recvMessage(recv);
957 | parseMessage(recv);
958 | m_ulSettingsAddr += 16;
959 | }
960 | }
961 |
962 | void HIDOpenUPS2::EraseConfigurationMemory()
963 | {
964 | unsigned char recv[32];
965 | int ret;
966 |
967 | m_ulSettingsAddr = OPENUPS2_SETTINGS_ADDR_START;
968 | sendMessage(OPENUPS2_MEM_ERASE, 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x40);
969 |
970 | int retries = 5;
971 | while ((ret = recvMessage(recv) <= 0) && retries > 0) {
972 | retries--;
973 | usleep(500);
974 | fprintf(stderr, "Erase 0x%02x retry %d/5\n", recv[0], retries);
975 | }
976 |
977 | if (retries <= 0) {
978 | fprintf(stderr, "Error waiting for erase operation to finish\n");
979 | } else {
980 | fprintf(stderr, "Successfully erased (0x%02x) configuration memory\n", recv[0]);
981 | }
982 | }
983 |
984 | void HIDOpenUPS2::WriteConfigurationMemory()
985 | {
986 | unsigned char recv[32];
987 | int ret;
988 |
989 | m_ulSettingsAddr = OPENUPS2_SETTINGS_ADDR_START;
990 | while (m_ulSettingsAddr < OPENUPS2_SETTINGS_ADDR_END)
991 | {
992 | sendMessageWithBuffer(OPENUPS2_MEM_WRITE_OUT, 16, m_chPackages+(m_ulSettingsAddr-OPENUPS2_SETTINGS_ADDR_START), 4, m_ulSettingsAddr & 0xFF, (m_ulSettingsAddr >> 8) & 0xFF, 0x00, 0x10);
993 | ret = recvMessage(recv);
994 |
995 | if (ret <= 0 || recv[0] != OPENUPS2_MEM_WRITE_IN) {
996 | fprintf(stderr, "Error (%d, 0x%02x) writing configuration variables , aborting ...\n", ret, recv[0]);
997 | break;
998 | } else {
999 | //fprintf(stderr, "Wrote page 0x%lx of configuration\n", m_ulSettingsAddr);
1000 | }
1001 | m_ulSettingsAddr += 16;
1002 | }
1003 | }
--------------------------------------------------------------------------------
/src/lib/HIDOpenUPS2.h:
--------------------------------------------------------------------------------
1 | #ifndef _HIDOPENUPS2_H_
2 | #define _HIDOPENUPS2_H_
3 |
4 | #include "HIDInterface.h"
5 | #include "usbhid.h"
6 |
7 | #include "HArray.h"
8 | #include "HLock.h"
9 | #include "HList.h"
10 |
11 | #define OPENUPS2_MSG_READ 32
12 | #define OPENUPS2_MSG_WRITE 32
13 |
14 | #define MAX_BUF 500
15 |
16 | #define ETA_CHG (double)0.90
17 |
18 | #define STATUS_OK 0x00
19 | #define STATUS_ERASE 0x01
20 | #define STATUS_WRITE 0x02
21 | #define STATUS_READ 0x03
22 | #define STATUS_ERROR 0xff
23 |
24 | #define OPENUPS2_GET_ALL_VALUES 0x81
25 | #define OPENUPS2_RECV_ALL_VALUES 0x82
26 | #define OPENUPS2_CLOCK_OUT 0x83
27 | #define OPENUPS2_CLOCK_IN 0x84
28 | #define OPENUPS2_GET_ALL_VALUES_2 0x85
29 | #define OPENUPS2_RECV_ALL_VALUES_2 0x86
30 | #define OPENUPS2_GET_ALL_VALUES_3 0x87
31 | #define OPENUPS2_RECV_ALL_VALUES_3 0x88
32 | #define OPENUPS2_CMD_OUT 0xB1
33 | #define OPENUPS2_CMD_IN 0xB2
34 | #define OPENUPS2_MEM_READ_OUT 0xA1
35 | #define OPENUPS2_MEM_READ_IN 0xA2
36 | #define OPENUPS2_MEM_WRITE_OUT 0xA3
37 | #define OPENUPS2_MEM_WRITE_IN 0xA4
38 | #define OPENUPS2_MEM_ERASE 0xA5
39 |
40 | #define OPENUPS2_ENTER_BOOTLOADER_OUT 0xA9
41 | #define OPENUPS2_ENTER_BOOTLOADER_IN 0xAA
42 |
43 | #define INTERNAL_MESG 0xFF
44 | #define INTERNAL_MESG_DISCONNECTED 0x01
45 |
46 | //commands
47 | #define OPENUPS2_DCMD_FORCE_PRECHARGE 0x01
48 | #define OPENUPS2_DCMD_ENABLE_CHARGE 0x02
49 | #define OPENUPS2_DCMD_PSW 0x03
50 | #define OPENUPS2_DCMD_RELAY 0x04
51 | #define OPENUPS2_DCMD_ENABLE_OUTPUT 0x05
52 | #define OPENUPS2_DCMD_LDO_EN 0x07
53 |
54 | #define OPENUPS2_DCMD_CELL1_DISCHARGE 0x11
55 | #define OPENUPS2_DCMD_CELL2_DISCHARGE 0x12
56 | #define OPENUPS2_DCMD_CELL3_DISCHARGE 0x13
57 | #define OPENUPS2_DCMD_CELL4_DISCHARGE 0x14
58 | #define OPENUPS2_DCMD_CELL5_DISCHARGE 0x15
59 | #define OPENUPS2_DCMD_CELL6_DISCHARGE 0x16
60 |
61 | #define OPENUPS2_DCMD_SPI_CS_AD 0x21
62 | #define OPENUPS2_DCMD_SPI_CS_VBAT 0x22
63 | #define OPENUPS2_DCMD_SPI_CS_TEMP 0x23
64 | #define OPENUPS2_DCMD_SPI_CS_VOUT 0x24
65 | #define OPENUPS2_DCMD_SPI_CLK 0x25
66 | #define OPENUPS2_DCMD_SPI_DATAOUT 0x26
67 |
68 | #define OPENUPS2_DCMD_SPI_WRITE_DATA_VOL_VOUT 0x30
69 | #define OPENUPS2_DCMD_SPI_READ_DATA_VOL_VOUT 0x31
70 | #define OPENUPS2_DCMD_SPI_INC_VOL_WIPER_VOUT 0x32
71 | #define OPENUPS2_DCMD_SPI_DEC_VOL_WIPER_VOUT 0x33
72 | #define OPENUPS2_DCMD_SPI_VOUT_STAT 0x34
73 | #define OPENUPS2_DCMD_SPI_VOUT_TCON 0x35
74 |
75 | #define OPENUPS2_DCMD_SPI_READ_AD_CHANNEL 0x36
76 |
77 | #define OPENUPS2_DCMD_SPI_WRITE_DATA_VOL_VCHG 0x40
78 | #define OPENUPS2_DCMD_SPI_READ_DATA_VOL_VCHG 0x41
79 | //#define OPENUPS2_DCMD_SPI_INC_VOL_WIPER_VCHG 0x42
80 | //#define OPENUPS2_DCMD_SPI_DEC_VOL_WIPER_VCHG 0x43
81 | #define OPENUPS2_DCMD_I2C_INC_VOL_WIPER_VCHG_ROUGH 0x45
82 | #define OPENUPS2_DCMD_I2C_DEC_VOL_WIPER_VCHG_ROUGH 0x46
83 | #define OPENUPS2_DCMD_I2C_INC_VOL_WIPER_VCHG_FINE 0x47
84 | #define OPENUPS2_DCMD_I2C_DEC_VOL_WIPER_VCHG_FINE 0x48
85 |
86 | #define OPENUPS2_DCMD_LED 0x50
87 | #define OPENUPS2_DCMD_READ_AFE 0x51
88 | #define OPENUPS2_DCMD_WRITE_AFE 0x52
89 |
90 | #define OPENUPS2_DCMD_SET_UVP_LOW 0x70
91 | #define OPENUPS2_DCMD_SET_UVP_HIGH 0x71
92 |
93 | #define OPENUPS2_DCMD_SET_OUTPUT_FREQUENCY 0x72
94 | #define OPENUPS2_DCMD_SET_CHARGE_FREQUENCY 0x73
95 |
96 | #define OPENUPS2_DCMD_RESTART_UPS 0xAA
97 |
98 | //For reading out memory
99 | #define TYPE_CODE_MEMORY 0x00
100 | #define TYPE_EPROM_EXTERNAL 0x01
101 | #define TYPE_EPROM_INTERNAL 0x02
102 | #define TYPE_CODE_SPLASH 0x03
103 |
104 | #define FLASH_REPORT_ERASE_MEMORY 0xF2 // AddressLo : AddressHi : AddressUp (anywhere inside the 64 byte-block to be erased)
105 | #define FLASH_REPORT_READ_MEMORY 0xF3 // AddressLo : AddressHi : AddressUp : Data Length (1...32)
106 | #define FLASH_REPORT_WRITE_MEMORY 0xF4 // AddressLo : AddressHi : AddressUp : Data Length (1...32) : Data....
107 | #define KEYBD_REPORT_ERASE_MEMORY 0xB2 // same as F2 but in keyboard mode
108 | #define KEYBD_REPORT_READ_MEMORY 0xB3 // same as F3 but in keyboard mode
109 | #define KEYBD_REPORT_WRITE_MEMORY 0xB4 // same as F4 but in keyboard mode
110 | #define KEYBD_REPORT_MEMORY 0x41 // response to b3,b4
111 |
112 | #define IN_REPORT_EXT_EE_DATA 0x31
113 | #define OUT_REPORT_EXT_EE_READ 0xA1
114 | #define OUT_REPORT_EXT_EE_WRITE 0xA2
115 |
116 | #define IN_REPORT_INT_EE_DATA 0x32
117 | #define OUT_REPORT_INT_EE_READ 0xA3
118 | #define OUT_REPORT_INT_EE_WRITE 0xA4
119 |
120 | ///// MEASUREMENT CONSTANTS
121 | #define OPENUPS2_CT_RW (double)75
122 | #define OPENUPS2_CT_RP (double)5000
123 | #define OPENUPS2_CT_R1_VOUT (double)56200
124 | #define OPENUPS2_CT_R2_VOUT (double)2740
125 | #define OPENUPS2_CT_V_FEEDBACK_OUT (double)1.2
126 |
127 | #define CHECK_CHAR (unsigned char)0xAA //used for line/write check
128 |
129 | #define MAX_MESSAGE_CNT 256
130 |
131 | #define TERMISTOR_CONSTS 34
132 |
133 | #define OPENUPS2_SETTINGS_ADDR_START 0x007800
134 | #define OPENUPS2_SETTINGS_ADDR_END 0x007C00
135 | #define OPENUPS2_SETTINGS_ADDR_CALIBR_START 0x0078D0
136 | #define OPENUPS2_SETTINGS_ADDR_CALIBR_END 0x007900
137 | #define OPENUPS2_SETTINGS_PACKS 64
138 | #define OPENUPS2_SCRIPTS_PACKS 4
139 | #define OPENUPS2_SCRIPT_START_ADDR 0x3C00//3F80
140 | #define OPENUPS2_SCRIPT_END_ADDR 0x3BF0
141 |
142 |
143 | class HIDOpenUPS2 : public HIDInterface
144 | {
145 | public:
146 | HIDOpenUPS2(USBHID *d);
147 | virtual ~HIDOpenUPS2();
148 |
149 | void parseMessage(unsigned char *msg);
150 | void printValues();
151 | void printConfiguration();
152 |
153 | bool readOneValue(char *str, int nReadMode, double dMultiplier, int len, unsigned char &c1, unsigned char &c2, unsigned char &c3, unsigned char &c4);
154 | float convertOneValue2Float(unsigned char *buffer, int nLen, int nIndex, int nReadMode, double dMultiplier);
155 | void convertOneValue2String(char *destination, int nLen, int nIndex, int nReadMode, double dMultiplier);
156 | bool setVariableData(int mesg_no, char *str);
157 | ATXMSG* GetMessages();
158 | double GetConstant(int i);
159 | unsigned int* GetTermistorConsts();
160 | int GetMessageIdxByName(const char* name);
161 | unsigned char getUPSVariableData(unsigned int cnt, char *name, char *value, char *unit, char *comment);
162 | void restartUPS();
163 | void restartUPSInBootloaderMode();
164 | void setVOutVolatile(float vout);
165 | void incDecVOutVolatile(unsigned char inc);
166 |
167 | void GetStatus();
168 | void ReadConfigurationMemory();
169 | void EraseConfigurationMemory();
170 | void WriteConfigurationMemory();
171 |
172 |
173 | private:
174 | float m_fVIN;
175 | float m_fVBat;
176 | float m_fVOut;
177 | float m_fCCharge;
178 | float m_fCDischarge;
179 | float m_fTemperature[3];
180 | float m_fVCell[3];
181 | float m_fVDuty;
182 | unsigned char m_bCheck23[8];
183 | unsigned char m_bCheck24[8];
184 | unsigned char m_bCheck25[8];
185 |
186 | unsigned char m_nVerMajor;
187 | unsigned char m_nVerMinor;
188 |
189 | unsigned char m_nState; //offline,usb,batpowered,vinpowered
190 |
191 | unsigned char m_bStateUPS;
192 | unsigned char m_bStateCHG;
193 | unsigned char m_bStateDBG;
194 | unsigned char m_bCapacity;
195 | unsigned char m_bBatOn[3];
196 | unsigned int m_nRTE;
197 | unsigned char m_bUPSMode;
198 |
199 | unsigned char m_bVOutPot;
200 | unsigned char m_bConfigSwitch;
201 |
202 | unsigned char m_bDBG[DBG_LEN]; //4 byte + 8 byte + 4 byte
203 | unsigned char m_bDBG2[30];
204 |
205 | unsigned int m_nShutdownType; //from FW 1.4
206 |
207 | unsigned char m_chStateMachine;
208 | unsigned char m_nLoadCfgStatus;
209 | unsigned char m_nSaveCfgStatus;
210 | };
211 |
212 | #endif
213 |
--------------------------------------------------------------------------------
/src/lib/usbhid.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | */
7 |
8 | #include "usbhid.h"
9 | #include
10 | #include
11 | #include
12 |
13 | #undef DEBUG_RECV
14 |
15 | USBHID::USBHID(unsigned int vendorid, unsigned int productid, unsigned int max_transfer_size, unsigned int index)
16 | {
17 | this->vendorid = vendorid;
18 | this->productid = productid;
19 | this->index = index;
20 | this->device = NULL;
21 | this->handle = NULL;
22 | this->connected = false;
23 | this->max_transfer_size = max_transfer_size;
24 |
25 | usb_init();
26 | usb_set_debug(0);
27 | usb_find_busses();
28 | usb_find_devices();
29 | }
30 |
31 | USBHID::~USBHID() {}
32 |
33 | struct usb_dev_handle *USBHID::open(void)
34 | {
35 | char buf[65535];
36 |
37 | this->device = this->find();
38 |
39 | if (this->device == NULL)
40 | {
41 | fprintf(stderr, "Device [0x%04x: 0x%04x] not detected !\n", this->vendorid, this->productid);
42 | return NULL;
43 | }
44 | this->handle = usb_open(this->device);
45 | int ret = usb_get_driver_np(this->handle, 0, buf, sizeof(buf));
46 |
47 | if (ret == 0)
48 | {
49 | fprintf(stderr, "interface 0 already claimed by driver \"%s\", attempting to detach it\n", buf);
50 | ret = usb_detach_kernel_driver_np(this->handle, 0);
51 | fprintf(stderr, "usb_detach_kernel_driver_np returned %d\n", ret);
52 | }
53 | usleep(1000);
54 |
55 | ret = usb_claim_interface(this->handle, 0);
56 | if (ret != 0)
57 | {
58 | printf("claim failed with error %d\n", ret);
59 | return NULL;
60 | }
61 |
62 | ret = usb_set_altinterface(this->handle, 0);
63 |
64 | fprintf(stdout, "Product: %s, Manufacturer: %s, Firmware Version: %s\n", this->getProduct(), this->getManufacturer(), this->getSerial());
65 |
66 | connected = true;
67 | return this->handle;
68 | }
69 |
70 | struct usb_device *USBHID::find(void)
71 | {
72 | struct usb_bus *bus;
73 | unsigned int i = 0;
74 |
75 | for (bus = usb_get_busses(); bus; bus = bus->next)
76 | {
77 | struct usb_device *dev;
78 | for (dev = bus->devices; dev; dev = dev->next)
79 | {
80 | if (dev->descriptor.idVendor == this->vendorid && dev->descriptor.idProduct == this->productid)
81 | {
82 | if (i == this->index)
83 | {
84 | return dev;
85 | } else {
86 | i++;
87 | }
88 | }
89 | }
90 | }
91 | return NULL;
92 | }
93 |
94 | char *USBHID::getProduct()
95 | {
96 | int len;
97 | int BUFLEN = 256;
98 | char *buffer;
99 |
100 | buffer = (char *)malloc(BUFLEN);
101 |
102 | if (buffer == NULL)
103 | return NULL;
104 |
105 | if (this->device->descriptor.iProduct)
106 | {
107 | len = usb_get_string_simple(this->handle, this->device->descriptor.iProduct, buffer, BUFLEN);
108 |
109 | if (len > 0)
110 | return buffer;
111 | }
112 |
113 | free(buffer);
114 | return NULL;
115 | }
116 |
117 | char *USBHID::getManufacturer()
118 | {
119 | int len;
120 | int BUFLEN = 256;
121 | char *buffer;
122 |
123 | buffer = (char *)malloc(BUFLEN);
124 |
125 | if (buffer == NULL)
126 | return NULL;
127 |
128 | if (this->device->descriptor.iManufacturer)
129 | {
130 | len = usb_get_string_simple(this->handle, this->device->descriptor.iManufacturer, buffer, BUFLEN);
131 |
132 | if (len > 0)
133 | return buffer;
134 | }
135 |
136 | free(buffer);
137 | return NULL;
138 | }
139 |
140 | char *USBHID::getSerial()
141 | {
142 | int len;
143 | int BUFLEN = 256;
144 | char *buffer;
145 |
146 | buffer = (char *)malloc(BUFLEN);
147 |
148 | if (buffer == NULL)
149 | return NULL;
150 |
151 | if (this->device->descriptor.iSerialNumber)
152 | {
153 | len = usb_get_string_simple(this->handle, this->device->descriptor.iSerialNumber, buffer, BUFLEN);
154 |
155 | if (len > 0)
156 | return buffer;
157 | }
158 |
159 | free(buffer);
160 | return NULL;
161 | }
162 |
163 | int USBHID::readInterrupt(unsigned char *buff, int size)
164 | {
165 | if (buff == NULL) {
166 | fprintf(stderr, "Invalid read buffer, skipping read\n");
167 | return -1;
168 | }
169 |
170 | int ret = usb_interrupt_read((usb_dev_handle *)this->handle, USB_ENDPOINT_IN + 1,
171 | reinterpret_cast(buff), size,
172 | USBHID_RECV_TIMEOUT);
173 |
174 | #ifdef DEBUG_RECV
175 | for (int j = 0; j < size; j++) {
176 | fprintf(stderr, " 0x%02x ", buff[j]);
177 | }
178 | fprintf(stderr, "\n");
179 | #endif
180 | if (ret < 0)
181 | fprintf(stderr, "hid_interrupt_read failed with return code %d\n", ret);
182 |
183 | return ret;
184 | }
185 |
186 | int USBHID::readInterrupt(unsigned char *buff) {
187 | return readInterrupt(buff, max_transfer_size);
188 | }
189 |
190 | int USBHID::writeInterrupt(unsigned char *buff, int size, bool use_transfer_size)
191 | {
192 | if (buff == NULL) {
193 | fprintf(stderr, "Invalid write buffer, skipping write\n");
194 | return -1;
195 | }
196 |
197 | unsigned char *packet;
198 | if (use_transfer_size) {
199 | if ((packet = (unsigned char*) malloc(max_transfer_size)) == NULL) {
200 | return -2;
201 | }
202 |
203 | if (size > max_transfer_size) {
204 | fprintf(stderr, "Warning: Multi-packet write not implemented !");
205 | size = max_transfer_size;
206 | }
207 | memset(packet, 0x00, max_transfer_size);
208 | memcpy(packet, buff, size);
209 | size = max_transfer_size;
210 | } else {
211 | packet = buff;
212 | }
213 |
214 |
215 | int ret = usb_interrupt_write((usb_dev_handle *)this->handle, USB_ENDPOINT_OUT + 1,
216 | reinterpret_cast(packet), size,
217 | USBHID_SEND_TIMEOUT);
218 |
219 | if (ret < 0)
220 | fprintf(stderr, "hid_interrupt_write failed with return code %d\n", ret);
221 |
222 | if (use_transfer_size) {
223 | free(packet);
224 | }
225 |
226 | return ret;
227 | }
228 |
229 | bool USBHID::isOpened() {
230 | return connected;
231 | }
232 |
233 | int USBHID::release()
234 | {
235 | int ret = -1;
236 | ret = usb_release_interface(this->handle, 0);
237 | if (ret < 0)
238 | printf("failed to release interface: %d\n", ret);
239 |
240 | return ret;
241 | }
242 |
243 | int USBHID::close()
244 | {
245 |
246 | release();
247 | int ret = usb_close(this->handle);
248 |
249 | if (ret < 0)
250 | {
251 | fprintf(stderr, "hid_close failed with return code %d\n", ret);
252 | return 1;
253 | }
254 |
255 | connected = false;
256 | return 0;
257 | }
258 |
--------------------------------------------------------------------------------
/src/lib/usbhid.h:
--------------------------------------------------------------------------------
1 | #ifndef _USBHID_H_
2 | #define _USBHID_H_
3 |
4 | #include
5 |
6 | #define USBHID_RECV_TIMEOUT 1000
7 | #define USBHID_SEND_TIMEOUT 1000
8 |
9 | class USBHID {
10 | public:
11 | USBHID(unsigned int vendorid, unsigned int productid, unsigned int max_transfer_size, unsigned int index);
12 | ~USBHID();
13 |
14 | struct usb_dev_handle* open(void);
15 | char *getProduct();
16 | char *getManufacturer();
17 | char *getSerial();
18 | bool isOpened();
19 | int readInterrupt(unsigned char *buff);
20 | int readInterrupt(unsigned char *buff, int size);
21 | int writeInterrupt(unsigned char *buff, int size, bool use_transfer_size);
22 | int close();
23 |
24 | private:
25 | unsigned int id;
26 | unsigned int vendorid;
27 | unsigned int productid;
28 | unsigned int max_transfer_size;
29 | unsigned int index;
30 | bool connected;
31 | struct usb_device *device;
32 | struct usb_dev_handle *handle;
33 |
34 | struct usb_device *find(void);
35 | int release();
36 | };
37 |
38 | #endif //_USBHID_H_
39 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Author: Nicu Pavel
3 | * Copyright (c) 2020 Mini-Box.com
4 | * Licensed under the terms of the GNU Lesser General Public License v2.1
5 | * http://www.opensource.org/licenses/lgpl-2.1.php
6 | */
7 |
8 |
9 | #include "devices.h"
10 | #include "version.h"
11 | #include "util.h"
12 | #include "usbhid.h"
13 | #include "HArray.h"
14 | #include "HIDDCDCUSB.h"
15 | #include "HIDOpenUPS.h"
16 | #include "HIDOpenUPS2.h"
17 | #include "HIDNUCUPS.h"
18 | #include "HIDInterface.h"
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 |
28 | static const struct deviceId* findDeviceByName(const char *name)
29 | {
30 | for(int i = 0; deviceIds[i].name != NULL; i++) {
31 | if (strcasecmp(name, deviceIds[i].name) == 0) {
32 | return &deviceIds[i];
33 | }
34 | }
35 |
36 | return NULL;
37 | }
38 |
39 | static int printDeviceCommands(const struct deviceCmd *cmds)
40 | {
41 | int i;
42 |
43 | if (!cmds) {
44 | return 0;
45 | }
46 | fprintf(stderr, "\n");
47 | for (i = 0; cmds[i].cmd != NULL; i++) {
48 | fprintf(stderr, "\t%s - %s\n", cmds[i].cmd, cmds[i].desc);
49 | }
50 |
51 | return i;
52 | }
53 |
54 | int usage(char *progname)
55 | {
56 | fprintf(stderr,
57 | "Usage: %s []\n"
58 | "Options:\n"
59 | " -t : Select device model (see below)\n"
60 | " -n : Select nth connected device of this model (default 0)\n"
61 | " -i : Write settings from this file. Warning: Will reboot UPS!\n"
62 | " -o